Objective-C/Cocoa Tips

[Updated 7 November 2010.]

I’ve been writing Objective-C and Cocoa code for almost 8 years, but it feels like a lot longer. There is good reason for that and it’s that during that time I haven’t just worked on projects during the day, I worked on projects at night, so the 8 years is more like 10-12 years of writing code. During that time, I’ve written a ton of code and come up with a lot of tips to help me. Each developer has his/her own style, so I’m sure that some people will take objection to some of what I say. However, a number of projects I’ve worked on could really have used this information to be more solid.

Without further ado:

  1. When you start a project, set the Organization in the project so that all new files created have the correct copyright. Pretty much every project I’ve worked on has most of the files stamped with the wrong copyright.
  2. Never use [self autorelease] or [self release]. These make code very hard to trace and WILL lead to a crash when another developer comes along and releases an object thinking there is a memory leak. Code should be restructured so that whoever instantiated the object releases it. In addition, CLANG will flag objects that aren’t released as a potential memory leak and having to sift through the CLANG messages every time analyze is done.
  3. Follow Apple’s guidelines on method naming. Methods that return an object that must be released by the caller must begin with “alloc” or “new” or contain “copy” in it. (create also seems to be in there as well). Methods that return an autoreleased object must NOT start with “alloc” or “new” or contain “copy” in it. These are important so that CLANG can analyze your code and not flag things that require you to have to go through each issue and trace the code.
  4. Never subclass core objects such as NSString, NSArray, NSSet, etc. If you need additional functionality either use a category or make a new class that has the object as part of it.
  5. If an object is added to an array, an NSOperationQueue, etc., make sure the object is released or autoreleased.
  6. Don’t use [NSObject new]; use [[NSObject alloc] init]; This is simply for readability. While they are equivalent, every time I see an alloc/init, I know I have to do a release or autorelease. “new” isn’t as widely used and someone could accidentally do self.object = [NSObject new] which would cause a memory leak if the object property is retained.
  7. Use assign properties for objects in limited cases such as XMLNodes or delegates. In most cases properties should be retained or copied.
  8. Don’t do a lot in the init methods. Also, be careful of using self.xxxx in an init method as there can be side effects in the assignment if you are using KVO or have your own setter. (The side effects part comes right from a WWDC presentation.)
  9. For properties that are retained, don’t do self.xxx = [[NSObject alloc] init]. This is a memory leak. You can add autorelease to the end or do the assignment in a few steps, i.e.
    	NSObject *obj = [[NSObject alloc ] init];
    	self.xxx = obj;
    	[obj release];

    If you don’t want KVO, you can eliminate using self. However, make sure you release the object before assigning it.

  10. Only use threading if you absolutely must and use NSOperations as much as possible. Threading is tricky and I’ve only seen a few places where it has been done right.
  11. Never make different build products using scripts, define values, and configurations. Use build targets. If you don’t, your project file will change all the time and people will keep checking in a slightly modified project file.
  12. Don’t put unnecessary things in the .pch file, create a Defines.h for that. Use the .pch file for including other files, one of which can be Defines.h. Put the Defines.h at the top of the project to make it easier accessible.
  13. Don’t name every file in the project with the same prefix. If you are sharing code with other projects, then making those have the same prefix, that is fine, but if you have 100 files that start with SG, it becomes quite annoying.
  14. Don’t sprinkle UNIX like code everywhere and don’t use UNIX calls when Cocoa calls are available.
  15. If you use CoreData, first read as much as you can about CoreData. Second, explicitly set the managedObjectModel and don’t let the OS do it for you. The OS will merge all the managed object models in your bundle and create 1 model. This is problematic when you have to do migrations.
  16. If using CoreData, use the XML data store until you are comfortable with the data model. Verify the metadata in the XML before you switch to sqlite.
  17. Networking code is hard. Make sure you don’t use synchronous calls even in threads as you can’t handle cancels. Use NSOperations for the calls. Debugging the code is even harder than writing, so don’t over complicate it.
  18. Don’t do version numbering in Info.plist files as you have to update 3-5 items each time you change the version number. See my post on an easy way to do this.
  19. Don’t use NSAssert everywhere. Do actual error checking and handle conditions where necessary. While NSAsserts are usually compiled out for release builds, NSAsserts just annoy me to no end and basically serve no purpose to me.
  20. Make sure builds compile with no warnings. Don’t ship code with build warnings unless there is some type of compiler issue preventing this from happening.
  21. Don’t turn on unnecessary flags in Xcode; making warnings into errors makes it hard to test code. If you abide by the item above, you don’t need to make warnings into errors.
  22. Before checking in files, always do a diff from your changes to what’s in source control. Don’t check in files just because you change 4 spaces into a tab. This also helps review the code and make sure you didn’t leave in test code.
  23. Never store a value using NSNotFound. Seems pretty obvious to me. Apple says:

    Prior to Mac OS X v10.5, NSNotFound was defined as 0x7fffffff. For 32-bit systems, this was effectively the same as NSIntegerMax. To support 64-bit environments, NSNotFound is now formally defined as NSIntegerMax. This means, however, that the value is different in 32-bit and 64-bit environments. You should therefore not save the value directly in files or archives.

  24. Never release an NSOperationQueue from within an NSOperation. That’s like pulling the rug out from under you while you’re standing on it!
  25. Be careful with type casting.
    NSUInteger value = (NSUInteger) -1;

    could have very strange consequences. Likewise, doing:

    NSUInteger value = (NSUInteger) NSNotFound;

    May not do what you want it to do and the results could be different on 32 and 64 bit machines.

These are just some tips that I’ve learned/developed over the years. I write a lot of code and have been involved in a number of projects that need TLC. Feel free to comment if you have more tips or want to debate anything I have written; these aren’t the be all, end all to writing code, but they do help me write code that will stand the test of time (I recently got involved in a project that has code I wrote over 6 years ago that is still in use!)

 

One Reply to “Objective-C/Cocoa Tips”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.