I've published some code on GitHub that I'm calling BTIConcepts
. There is nothing earth-shattering here; I'm certainly not breaking any new ground in computer science. This is just a variety of small things that are individually useful, and become even more so when combined. If you have ever thought "Boy, I sure wish all of my [instances of class] would do [something]", then you might want to peek through the code. Even if you don't use any of these specific classes, surely there is a weak spot somewhere in your repertoire where you can apply the concepts.
But first let's talk about coding style.
I have written previously
about how I use logging. I've even written a little service
to make adding these logs easier. This is caveman logging at its finest, and I am not even a little bit ashamed of it. The number of problems I have solved over the years simply by doing this more than outweighs any loss of "l33t" status I may have as a professional developer. I've added to this recently with BTITrackingLog. I'm not the first to make an NSLog macro, and probably won't be the last. But until I did this, I was only using NSLogs. So it was either ALL messages, or no messages. Now I can conditionally enable the tracking logs, but still see "important" NSLogs. I will add a configuration and scheme called Debug + Tracking that allows me to see everything. I've thoughtfully commented them out from the repository in anticipation of people viewing them as annoying, but what can I say, I like them. I sadly have not yet updated my service to use BTITrackingLogs.
For categories, method names should be decorated somehow to avoid clashes with primary methods. Most people use a prefix, so btiDoSomething, for example. I saw a recommendation a while back (sorry person, I'd credit you if I remembered who you are) to use postfixes instead. doSomethingBTI. They key argument offered was that with a prefix, you have to know that you mean to use a category method when you start typing in order to get code completion. So it's not merely "This is an array method", it is also "This is an array method that I or someone else have manually created". With a postfix, just start typing like you normally would, and code completion will take care of the rest. For methods that accept multiple parameters, I place the decoration at the end of the first group of text, like so: doSomethingBTI:andSomethingElse:andThisToo: Another benefit (or curse, depending on point of view) of using a postfix is that if the category method is similar to a primary method, then you can easily change your mind while typing by hitting either method in the pop-up. Can't do that with a prefix.
The ways that I have organized my view controllers have become less and less important as Objective-C gains new features. I saw a recommendation a long time ago to put dealloc at the top of the file, so that it is right there next to the properties. This way when you add a property, you're less likely to forget to travel all the way to the bottom to clear out the property in dealloc. And since viewDidUnload was often very similar to dealloc, I wanted it placed right after dealloc for the same reasons. Well, with ARC you don't need dealloc (in most cases) anymore. And you don't need to synthesize properties anymore. And viewDidUnload won't be called anymore. So, yeah. But there are still a few of my organization quirks that remain. I like notification responders grouped, and UI response methods (IBActions, etc) grouped, and so on. Delegate methods always go to the bottom, though if multiple delegate protocols are in use, the specific order doesn't matter. The #pragma mark usage in the .m files here are quite typical of how I do things. I don't normally do it in the .h file quite so much, but wanted things to look nice for public consumption. The specific order of anything in these files doesn't really matter, but for sake of consistency, try to follow some kind of pattern.
As I said at the beginning, I often want all of [instances of class] to do something. Naturally this points to categories, and there are categories here, but categories don't cover everything. For example, if I always want view controllers to do something in viewWillAppear:, a category isn't going to accomplish that easily, if at all. For far too long, my solution to this has been to use templates. Every time I wanted a table view (I don't like UITableViewController), I'd fire up my template that contains a table view property, and be on my merry way. I even sorta made this work with XIBs in Xcode 4. I always got what I wanted, my code was consistent, and I was doing less work. Yay! The thing about templates is that you are effectively copy-pasting code. If you change your mind about how something should work, then all of the other hundreds of classes that were created by your template have to be changed. Or, most likely, don't get changed. Another issue with templates is that your non-standard templates don't keep up when Apple changes their templates. This is not to suggest that all of Apple's templates are winners, but if they start adding something or removing something, one should at least inquire if that is a noteworthy change. I would still be trucking along adding now-useless dealloc and viewDidUnload methods if I was continuing to use my templates. I've decided that it is useful to start with Apple's templates, quickly observe any changes, and then copy-paste in my style stuff (pragma marks, etc).
If templates are out, and categories don't cover everything, that pretty much leaves subclassing. Instead of always subclassing UIViewController and copy-pasting in what I want over and over again, make a new common view controller. I've made BTIViewController. And then all of my view controllers can inherit from it. Don't need viewDidUnload anymore? One class, delete one method, done. Need to update didReceiveMemoryWarning since viewDidUnload isn't called anymore? One class, modify one method, done (Ignoring customization in subclasses, of course). All subclasses then inherit that change. And although this is less relevant with iOS 6, want EVERY view controller in your project to have the same autorotation behavior? One class, one method, done. (Really wish I'd thought of this years ago...). And then differences are handled like any other subclass. Override any behaviors you want to change.
So the "meat" of BTIConcepts is really found in 3 classes: BTIObject, BTIViewController, and BTITableViewCell. They don't have to do much at all- BTIObject in particular does not - but by simply using them, downstream activities become much easier if you need to apply changes across the board. Instead of NSObject, inherit from BTIObject. Instead of UIViewController, inherit from BTIViewController. If you don't like what my classes do, then modify them or make your own. But the key concept here is to insert something as high in the class hierarchy as you can, so that you can easily apply mass changes if the need should arise. I would probably even go so far as to argue that you should do this even if your inserted class is totally blank. Prevents having to go change all of your inheritances later.BTIObject
- At the moment all it does is provide a custom description method. It automatically logs all properties. Before I came up with this, I had lengthy and quite manual description methods. This is easier.BTITableViewCell
- I hate worrying about the cell identifier, particularly with XIB-based cells. Now, (hat tip to Jeff LaMarche
) I no longer have to.BTIViewController
- Now we're getting into more functionality. First of all, I hate when other classes call initWithNibName: (or initWithStyle: for UITVC). That's bad OOP, IMO. The 'parent' VC has to know too much about this VC when doing this. Is there a nib? What name does it have? And if this VC is used in multiple places across an app, these decisions have to be made every single time. Sloppy, unnecessary work. So right off the bat I override init to pass through to initWithNibName:. Parents simply alloc/init, done.
I always want notification listeners to unsubscribe in dealloc. Done. The number of reasons to have dealloc in a view controller decreases even further.
The bulk of the remaining additions deal with BTINotificationHandlers, which I'll describe in a moment. This view controller will automatically register and unregister notifications at appropriate times. No more forgetting to unregister, or unregistering incorrectly.
Pre-iOS 6, if I wanted each view controller to have the same autorotation behavior, I'd define that behavior one time in this class. No more forgetting that VC's default to portait if you don't override the proper method. I didn't include it here since the code is aimed at iOS 6, but it would be easy enough to add.BTITableViewController
- This can be viewed as an alternative to UITableViewController, but I'm not really trying to recreate that class since I don't like it. I define basic table view stuff like a property and conform to protocols. Additionally, there are some features that get you started for handling things like selection and searching. Not very much code is implemented, it's more of a "here are some tools that should be useful" kind of thing.
Stick around for part 2...