iApps Development Blog
iApps Development Blog
The Joy in Discovering You Are an Idiot
When you’re a comparative newcomer to a programming environment, the simplest lessons are frequently the most painful to learn. In the end, you feel like an idiot for not recognizing the solution sooner, yet there is still a sense of accomplishment at having marked something in your brain that will never trick you again.
In one recent case, I was trying to replicate the behavior of the NSURLDownload class with NSURLConnection. I got into this jam in the first place because one of the documents in Apple’s iPhone Reference Library, titled URL Loading System, happily describes how to use either of the two classes. After reading through both chapters, I felt that NSURLDownload would suit my application more effectively because it should allow a one-step transfer of a file from a server to one of the application’s directories. And so, I wrote the code and had it working in the iPhone Simulator in no time.
By this point, more experienced iPhone SDK programmers know what happened next: I went to install the app on a device, and XCode lit up like a Christmas tree with all kinds of errors. It turned out that NSURLDownload is not supported in the iPhone OS (at least as of 2.2). Moreover, the docs had such good examples that I was able to make my code work swimmingly in the Simulator without ever having to look up the class reference. If I had tried to look up the class in the iPhone Reference Library, I would have found it in absentia.
Aside from a huge roll of my eyes — why doesn’t that URL Loading System document say that NSURLDownload isn’t available in the iPhone OS? — I remember having made some handwritten notes about using NSURLConnection, which requires bringing the data into a temporary holding spot (NSData), serializing it, and then writing it to a file (the file in question is a .plist).
And so, off I went to reinvent the wheel (ugh, I hate doing that) with NSURLConnection.
Now, having come from decades of pointerless (but not, I hope, pointless) programming, I find my brain occasionally passing gas when it comes to memory issues. Is it best to pre-allocate memory for a mutable object that offers the option, or wait and let the object figure it out as you append data later? Opting for preallocation — and using the wrong method to do it — became my undoing.
In applying NSURLConnection, you use one delegate method to accumulate incoming data in an NSMutableData object, and then another delegate method to do something with the accumulated data when the request has been completed. I knew that my data file was about 14,000 bytes, and in the NSURLConnection class documentation I saw the initWithLength: method. To begin, I initialized with a length of 10000.
Everything was going fine until it came time to serialize the NSMutableData object to an NSData object (via an NSPropertyListSerialization class method). The latter reported an error that the data was not valid XML at line 1. (In my experience with some other error reporting environments, a “line 1” error could mean anywhere, so I didn’t believe that location at first — one of those wounds from previous wars.) I double-checked the server file against an XML validator, and everything was OK there. I next looked at the accumulated NSData data (in the Console via NSLog()). The description method for an NSData object returns a hexadecimal representation of the data, so I wasn’t going to see my familiar XML strings. Hmm, there were bunches of zeros padding the start of the data.
What I failed to forecast in my negotiations with the NSMutableData class was that not only does initWithLength: fill the specified length with zeros, but the appendData: method (used to accumulate incoming data from the connection) starts appending after the zeros. “Append” really means “append.”
A “D’oh!” moment if ever there was one.
The solution lay in the NSMutableData initWithCapacity: method, which creates an object of zero length, ready to be appended where it needs to be: at the start of the data with no stinking zeros.
Despite making what turned out to be a rookie mistake, I still took a bit of pride in deducing the issue. Searches in various iPhone developer fora came up empty, so I was left to my own devices. I’m sure it helped, too, that after fixing the initialization business, the rest of my code for serializing and saving the incoming data worked as I had planned. True, I was an idiot, but in the end I felt good about having learned something along the way that will probably come in handy with other classes, too.
Monday, February 16, 2009