As far as I know, there were two main approaches to compress the data. The first one, more common, was to load the whole code and "de-crunch" it. It uses RLE or simple vocabulary algorithm (LZ-like). RLE was pretty common to compress screen (there is a lot of zero sequences in many cases).
The second one, less common, performs decompression during the loading. The best example I remember (Digi Synth demo) uses the Huffman-coded data and its loader decompresses its data on the fly. But RLE is suitable for this case too.
I should mention that some tape-copy programs use on-the-fly compression and decompression to fit the most data into the limited memory of ZX Spectrum (TF-Copy, BS-Copy, etc.) The most common compression method for that was the RLE, mentioned above.
I never met a software that uses the second method you mention to reduce 1s. The typical Z80 machine code has a pretty big "entropy", so XORing cannot reduce the number of 1s in any significant way. A more efficient way was to use a custom loader routine with some kind of "turbo" approach (typically shortens the length of pulses), but it has some disadvantages too (higher sensitivity to tape errors, etc.)