Never ever use
-[NSNotificationCenter removeObserver:]to unregister from notification events, except from a dealloc method. This might cancel registrations made by a parent class, and you cannot know how a parent class is implemented (at least you shouldn’t care). Stick to this rule even if your parent class is NSObject: Your class hierarchy might change in the future, and you do not want to run into problems when you don’t have to, do you?
(via Cédric Luthi)
I strongly disagree. There are multiple situation e.g. on the iPhone where registering and deregistering in the [will|did]Appear/Disappear: methods is valid, helpful and good style. (And I took the liberty to fix the classname)
Update: Permature posting is premature. (note to self: always read the whole referenced article).
So to add some actual wisdom to this subject: Calling the -[NSNotificationCenter removeObserver:] should be confined to situations where you get rid of the object. So I agree dealloc can be the place, but it also can be somewhere up in the chain if you invalidate your object where you already have the knowledge it should go away, but it is not dealloced memory wise. In complex cocoa apps you run in these situations. Also helpful in these cases: [NSObject cancelPreviousPerformRequestsWithTarget: self];
A common task for UIKit devs: Inset a CGRect by an amount from each edge:
And to do that in one line of course the constructor of UIEdgeInsets
Why on earth they decided to make the order of insets the other way round as CSS will remain a mysterie. Also note that a negative inset expands towards this edge. E.g.
results in the following CGRect:
Just a Quick list of what to keep in mind when designing your app with the new Fast App Switching in place:
- the entry point of your app might be anywhere and data may have changed. Even built in Apps like the Photos app get that one wrong. Have the Photos app open. Make new pictures. Go back. Or worse: download and delete all the fotos to iPhoto. Reopen the Photos app: crash.
- if something goes wrong, the app doesn’t automatically stop and restart when the user presses home. This might seem obvious, but if there are edge cases where you mess up your navigation controller hierachy beyond repair that matters. This happens. The user needs to know how to kill your app in that case, and most don’t. Be extra careful with that.
- besides the fast app switching way you still need to keep state and position in the app on your own for your next restart. For old devices as well as for when you get killed somewhere along the way. So you still need to do that. Defacto things got more complex instead of easier. However, if you do it right you might also have a good chance of being a good iOS 3.x citizen.
- if you listen to URLs or Push Notification you need to be prepared for getting them while already running. Worse: you need to detect this yourself using a combination of willEnterForeground and didBecomeActive, so you can handle things differently if you were active and not. Also in the URL case the possiblity exists you get called twice. See http://twitter.com/chockenberry/status/17889156210
- network connections and hosting die when you get suspended. Be sure to reopen your server if you e.g. provide web access or something similar.
- your app might not restart for very long time periods, make sure you move important checks and tasks to willEnterForeground or didBecomeActive so they don’t starve out.
That’s just a quick list of real world issues I encountered. I’m sure there a lots more subtleties and edge cases. Be prepared!
- if you have settings you need to make sure that changing those on the fly does work. e.g. the settings change can hit you in any place in your app and you need to be prepared and act like it. e.g. update lists, query the NSUserDefaults without caching, pp
- if opening an URL/push notification means you jump to another location in your app, the code handing that opening needs to be able to dismiss any - and i mean any - currently showing UI and present itself. This can be a very tough one.
With all the issues involved, opting out by adding UIApplicationExitsOnSuspend with a boolean value of YES to your Info.plist might be the better option if your app has great pre-iOS4 behaviour.
If you know me, you might know that I don’t like Xcode’s internal Documentation Viewer and don’t use the internal editor. My reasons: I need my Tasks separated. Source files are in SubEthaEdit, Project files, compilation, console feedback, debugger are in Xcode, Source control management is in the terminal, and Documentation is in AppKiDo (and AppKiDo-for-iPhone). Why? Quick and straight access. I hit the Xcode App Icon for a compile, AppKiDo for Documentation, etc. And they all remember their state. If I do all in Xcode then navigation becomes slow and cumbersome.
But that all has a side effect: I’m utterly lost in the time AppKiDo needs to catch up with the latest Xcode and documentation release if it breaks anything. Thanks to Andy Lee that is almost always a very very short period of time, but nevertheless makes me anxious all the time. That’s why I really Appreciate a recent contender in the Documentation Viewing business: Ingredients. It looks very nice and modern, but still lacks a few features I use in AppKiDo. Nevertheless: the more the merrier!
A IMHO quite underused feature of the format string is indexed parameter referencing. Something that can’t be found easily in the documentation sadly, but is quite crucial for good localization and can be also be used if you want to use an argument more than once:
NSString *formatString = @"Page %1$d of %2$d";
// results in @"Page 5 of 120"
If you use the indexed argument list, then you can easily change the order the arguments are used:
NSString *formatString = @"Total Pages:%2$d - Current Page:%1$d";
// results in @"Total Pages:120 - Current Page:5"
Or even use one value twice, or not at all:
NSString *formatString = @"(Total Pages:%2$d) %1$d/%2$d";
// results in @"(Total Pages:120) 5/120"
This works with all types of placeholders, just inject n$ between the % and the actual type of the identifier. 1$ is the first argument, 2$ the second and so on.
Usually life with Cocoa is nice. You have hooks and handles for every occasion. However - sometimes you are not looking in the right place, or have no idea this hook you need exists at all.
A strange and interesting example from SubEthaEdit is the
[NSAttributedString doubleClickAtIndex:] method. I had to ask for that at WWDC because I was sure this kind of functionality would either be named differently if put at the model level, or would be in
NSTextView. However, there it is, quite misplaced, but there.
Another case of using the right delegate callbacks on the iPhone is the
-[UIApplicationDelegate applicationDidFinishLaunching:] method. Coming from the desktop this is a no brainer, this is where you set up your application.
However, with iPhone 3.0 things changed. There now is a new call,
-[UIApplicationDelegate application: didFinishLaunchingWithOptions:] which is called in preference of the aforementioned call. This is clearly stated in the
UIApplicationDelegate overview in the Documentation. However these are the intricacies you might miss if you are not watchful. Always stay on top of the API changes!
And a last one: Make sure you understand what each hook means. Make sure you put your code in the right spot. Don’t do stuff in
-awakeFromNib if there is a better location. E.g. if you are a
-awakeFromNib might get called twice, once for the nib it lives in, once for the nib it itself is loading. Use
-windowDidLoad instead. If you are an
-viewDidMoveToSuperview methods may be more suited for setup than the init methods.
Bottom line: make sure you understand the events you get called for and think about if this is the right place to put your code. If not, it is highly likely Cocoa has the right hook for you, you just don’t know it. Think again of what kind of event it is you need do know about and look into the documentation. Don’t forget to look into all notifications as well, some events only trigger those.
Another element that is easily overlooked, but can make code and life so much easier: Convenience Constants. Take some examples:
CGRectInfinite- mostly used in Core Image contexts
CGRectNull- not equal to CGRectZero, returned by e.g. CGRectIntersection()
Of course it is. And greatly designed too if you ask me. However there is a huge world of regular functions and macros which you might have overlooked. Here are some of my favorites:
Look them up, they might prove useful, or just make your code easier to read!