My 2 cents in (hopefully) 5 minutes
I'm performing a 5 minute "lightning" talk at the iPad/iPhone DevCon tomorrow and I'm not planning to use slides. I don't want to burn half of the 5 minutes getting keynote working, but hopefully attendees would like to have some of the links and code I'll reference. So! Here are my notes, links, and other such fodder. There is a lot more than 5 minutes worth of material here as I've been making notes about my habits for the last week or so; this is all of them. I plan to talk until I run out of air or out of time...
Tiny Little Habits for the iOS Dev to Avoid Premature Baldness
A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines.-- R.W. Emerson (Essays. First Series. Self-Reliance.)
Here are some consistencies that I think are not foolish. These little habits have saved me a lot of stupid, often difficult to debug, mistakes.
When you alloc and init or otherwise instantiate an object that you now own, write the balancing release or autorelease immediately; this includes any IBOutlets and properties. If it's an IBOutlet, also write the [self setSomeIBOutletProperty:nil] in -viewDidUnload when you write the [someIBOutlet release] in -dealloc.Similarly
When something that is a delegate of something else, in the dealloc I'll tend to perform [somethingElse setDelegate:nil], whether I expect somethingElse to live for a while longer or not, then [somethingElse release]; on the next line. When you write [somethingElse setDelegate:something], write this code right away just like the alloc+init discipline above. This is one of the reasons I don't like to set delegates in xib files. It's too easy to mess this up. Also, I refactor as I work...Speaking of delegates and protocols
When you write a delegate protocol, make absolutely every method optional so you get into the habit of testing if the delegate responds to the selector you're about to call as a matter of course. You're a lot less likely to mess up the delegate protocol that way. When you conform to a protocol, copy all (required and optional) methods into your class and comment them out. Uncomment them as you need to use them. That way, you don't have to go back and forth to the other class's header file to see if there is a delegate method for some behavior you need to know about. You have it all at hand.Do as little setup work as possible in xibs, they work best for simply putting together UI geometry
- Avoid xib file dependencies (See this BNR blog post)
Avoid making view controllers in MainWindow.xib
- It's easy enough to make 'em in code. It's easier to know where things come from when you make them in code.
- Avoid setting delegates in xib files. Do it in code.
Lines of code are (mostly) free
Self-documenting code is better than heavily commented code and Objc is verbose by tradition; I think this is a good thing. Debugging code that isn't too nested is much easier.[self setFloozle:[floozaWhat whatWithString:[NSString stringWithFormat:@"jeeminy %@ Christmas, %@", frickin, virginiaName]];Where you gonna put a breakpoint in that crap?
When you think you can draw something in code instead of using a static image, try to find the time to implement drawing it in code. Also, Buy Opacity.app.
- When apple comes out with yet another screen size, you'll be (somewhat more) ready for it.
- When you want to change the size of something for some other reason, you don't have to generate a new image.
- You get to figure out how on the green Earth you designer does the crazy things they do in photoshop... only in Quartz.
Speed
If the profiler doesn't say it's slow, it's not slow. If the profiler says it's slow, it's slow. Use the profiler.Note: Shark (sniffle) is dead. Long live Shark! They killt it good in iOS 4.0 (no longer runs on the device).
Others' little code bits that are very useful:
- Red Sweater NSData thing that prints the hex for you
- IsEmpty from Wil Shipley with a small addition
static inline BOOL IsEmpty(id thing) { return thing == nil || ([thing isEqual:[NSNull null]]) //My addition for things like coredata || ([thing respondsToSelector:@selector(length)] && [(NSData *)thing length] == 0) || ([thing respondsToSelector:@selector(count)] && [(NSArray *)thing count] == 0); } - LogMethod() from Aaron Hillegass
//Modified from Aaron hillegass's code #define LogMethod() NSLog(@"-[%@ %s]", self, _cmd)
- CPU and Memory tools I use to see what's really going on that I know I stole from a skojillian places, but can't remember who to thank.
- GTMHTTPFetcher to download stuff (and Google Toolbox for Mac in general)
- Nathan Eror's Core Animation Toys.
Subclassin' -- You stay classy, San Diego
Subclass less, compose more. In subclasses, always call super's implementation of a method, even when you just *know* it does nothing. (I'm thinking of UIViewControllers here). That way, if you ever do decide to put in an intermediary class between your class and the former superclass, you don't get stuck wondering what happened when those methods no longer get called.Minimize direct access to the internals of your objects
Yeah, yeah, message send blah blah. It's not that slow unless you abuse it.- Write or synthesize accessors for ivars you want to share. Also, buy Accessorizer.app
- Remember, you can use readonly in a property when appropriate (and it is more often appropriate than you might think).
- Mark ivars you don't want messed with as @private. For srsly.
- Make good use of class continuations:
- to document the internal workings of your objects (If the header is the public documentation, the continuation is the private, implementation documentation)
- to suppress "may not respond to" compiler warnings
- to expand readonly properties to readwrite status internally
- to make a protected methods header file if you want subclasses, but not other users of a class, to use certain of its internal methods.
- Import that file in subclasses to suppress compiler warnings.
- Makes it easy to delineate private vs. protected vs. public methods
Know what you're really doing when you use dot syntax
I have a weird dot syntax habit, ymmv. There be message sends in them thar hills.I rarely return nil from something that is expected to return a string, dictionary, an array, or a set
Instead, I return an empty object of the expected type like so:
return @"";
return [NSArray array];
return [NSDictionary dictionary];
I often use Shipley's IsEmpty() to find out if some object is empty, rather than comparing its pointer to nil.
That way, nil messaging returning nil doesn't bite you in the ass when you're expecting something that is not an NSObject and you don't get that ugly (null) string in your UI. I hate that.
Make a logging method you can turn off and one you really can't; both call through to NSLog
Also, see the expansion of LogMethod from Aaron Hillegass#define LogMethod() SBLog(@"-[%@ %s]", self, _cmd) #define WarnMethod() SBWarn(@"-[%@ %s]", self, _cmd) #define SBWarn NSLog #ifdef DEBUG #define SBLog NSLog #else #define SBLog #endif
Run the static analyzer, but not all the time
I usually run it:- before a commit to source control
- after I make a new class
- any time I am alloc'ing a lot of stuff
If your code won't work unless some particular condition is satisfied, NSAssert that condition!
(Hi Joe P.) You can turn off asserts in shipping code, if you're a panzy. I leave 'em on. I would rather get a crashing bug I can reproduce than one I can't and it's probably gonna crash soon enough...NO WARNINGS (and turn on the warning set you can find on the blog the Rentzsch linked to that one time)
On External, third party, projects
- READ THE CODE
- UNDERSTAND THE CODE
- DON'T USE IT UNTIL YOU KNOW HOW IT WORKS (make a little test project to learn it and drop lots of breakpoints)
- If it's hard to read, it's probably broken.
- I'm on the three20 mailing list; you wouldn't believe some of the questions asked there...
- Add external files project relative outside of your project (makes updating the external project easier)
- Freeze external projects at a given revision number; update early on between releases:
- in svn this is done with gtmOauth -r 19 http://gtm-oauth.googlecode.com/svn/trunk in an svn:external
- Avoid editing external projects directly (makes it hard to merge), instead subclass or make categories to change behavior
- When possible, avoid static libraries
- Too many places where compiler flags are set.
- Instead, compile in only those parts you need and their dependencies
- I usually add just the top level source file I need and pull in dependencies as the compiler pukes, rinse and repeat until no errors or warnings.
Make tiny little test projects for new features
- As your codebase grows, new features can present a difficult integration, so split it into two steps
- 1. New Feature
- 2. Integrate new feature into codebase
- 3. goto 1
If there is any chance that you've got a delayed perform of a selector scheduled, don't forget to cancel those when appropriate
+[NSObject cancelPreviousPerformRequestsWithTarget:] +[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]Incolsolata, because menlo makes left square brackets look indented.
If you have to pick just one: Read Mike Ash's blog.



