Well, after my mad dash to port my procedural C program to a very basic Cocoa App in Xcode, I had a look at the code and thought ‘what a mess’. So, given that I’m trying to learn to program for the Mac, I thought I would start rewriting the entire app using Objective C and the various Cocoa frameworky things … so that it at least looked and operated like a ‘normal’ Mac app.
This has turned out to be much harder than the simple port I did earlier. A few key considerations;
- I don’t really know any OO language very well, so I am learning and doing in Objective C as I go.
- I’m not familiar with using any other GUI development environment for building an app … so I’m not entirely familiar with some of the ‘click and drag’ concepts in Xcode (actually Interface Builder). There are quite a lot of things you generally should do ‘graphically’ using ‘Interface Builder’ … and I get the impression that the authors of various Cocoa programming books assume that you’ve probably used some other OO based GUI design tool.
- There are quite a lot Cocoa concepts that I’ve never come across before such as Bindings, delegates, notifications.
- And keeping track of retain counts is a big issue for me.
My old app just read in a text file line by line and pulled out variable=value type lines in order to set various configuration options for the program. Of course, I wanted to rewrite this so that I had a proper preferences panel. This took me a long time. The basic stuff is relatively easy; ticking checkboxes, entering a value into a field etc. Even setting it up to read/write this stuff to a user defaults database is easy too.
But my app had a list of servers it connected to (one or more), and for each server you had a username and password and some connection details. This seemed a perfect fit for an onscreen table in a preferences panel. This is an NSTableView in Cocoa speak. And there is this magic thing called ‘Bindings’ to allow you to have a table on screen linked directly into an array of objects (each server line being some custom server object). With every column of your table having regular text, this is easy to do and there are quite a few examples on the net, but I wanted one table column to have a checkbox in it, and another field was for passwords, so I wanted that field to show asterisks onscreen.
Using Interface Builder I somehow got the column with the checkbox to have a checkbox in it… but honestly I can’t remember how I did it and haven’t worked out how to do it again (suffice to say, the way to do this stuff in Interface Builder does not seem that logical or easy to me). But the password column had me baffled for days. I never found an example on the net of how to do it (maybe I didn’t look hard enough), so I came up with some workaround solution that used delegate functions to erase the contents (visually) of the password column when a special ‘hide passwords’ checkbox was ticked. That took quite some time to work out the delegate function and then how to make the delegate function work out which column it was on and so on.
I always thought the password thing ‘must be simple’ since Interface Builder has a NSSecureTextField that does exactly what I want … but for a simple standalone input textfield. Eventually I had another go and finally got it work;
- In Interface Builder, have your NSTableView on screen and click on the column that you want passwords to appear in. You’ll need to click multiple times until just the works ‘Text Cell’ at the top of the column are highlighted in white by themself (ie. you don’t want the whole column highlighted, you just want a small white rectangle around the words ‘Text Cell’)
- Then bring up the object inspector thing (cmd-shift-i I think), and click on the tab towards the right that has an ‘i’ in a circle.
- It should say ‘Class Identity’ near the top.
- Now change the Class in the ‘Class Identity’ section to ‘NSSecureTextFieldCell’
I find I end up using delegates a lot. A good example is the ‘windowShouldClose’ delegate function. Basically, I had an issue where if you closed the preferences panel, sometimes all my updates to my server list table didn’t go through. So I wanted some way to write out the user data stuff when you clicked the close box on the preferences window. This is where delegates comes in. I needed my preferences controller object thing to be a delegate of the NSPanel for my preferences. But I guess the problem for me is always what thing am I delegating from and what thing am I delegating to? and what delegate functions will work in the delegate. Sure the developer docs do list the delegate functions for each class … but it still takes some trial and error to work it all out. I just put a lot of NSLog statements everywhere.
These are actually really useful. I end up having objects way down the object tree from my document controller class that need to communicate status info back to the document class (eg. socket connection has failed). Having no background in OO programming I have no idea of how you are ‘meant’ to pass status info back …. but notifications are great as its all asyncronous and you don’t need timer events in your event loop looking for status info. I’m still not sure if this is the right way to do things… but it does seem to simplify my code a lot.
I realise that with Leopard I now no longer have to worry about retain counts, but given that iphone app development still requires it (and I’d like to give iphone app development a go) I thought I should at least force myself to keep track of retain counts. Having said that, every couple of days I find some crazy bug in my app relating to over releasing of objects. Part of my problem has always been the mix of objects that I allocate and keep track of and so called Foundation classes that use an autorelease pool. When I first started, I wondered why the NSString class had seemingly duplicate methods. For example the initWithFormat and stringWithFormat methods;
NSString *a = [[NSString alloc] initWithFormat:@”count = %d”,n ];
NSString *a = [NSString stringWithFormat:@”count = %d”,n];
These two lines do much the same thing except the first one creates an object that you allocate and manage whereas the second one (I think) allocates an object but shoves it in the autorelease pool. Putting it in the autorelease pool means that if the retain count goes to zero on that object and the autorelease pool is being flushed (which happens at the end of the event loop for most programs) then the object can just disappear. I have been bitten by this many times. An object which I thought should still be around ends up being flushed. I find these problems quite difficult to trace and diagnose. I have gotten to know the ‘Instruments’ tool, but it only helps so far. I need to learn a bit more about the best way to manage retain counts.
Overall, I am ‘getting there’ slowly. There is still a lot of old C code in my program. A lot of stuff still using C strings where I probably should be using NSStrings to avoid bounds problems in the future. But those sorts of things can wait. In some cases using the Cocoa stuff makes some problems a lot easier. For example, my original program contained a double fork in order to get a spawned process to be independent of my program (I wanted to make sure the spawned program could still keep going if my program ended). This double fork was reasonably complicated and I also needed to communicate to this program at a singular point. Now, I just use openFile: withApplication: from the NSWorkspace class and it does away with my double fork entirely.