cocoa-dom

cocoa bits I use, come across, like, hate, the whole shebang.

twitter | adn         rant-dom | TCM        github         rss | archive
April 19, 2011 at 6:24am
0 notes

Didn’t we used to make fun of Eclipse for stuff like this?

— I could not agree more. (via The Powers of Observation)

April 7, 2011 at 5:49am
9 notes
Reblogged from rentzsch

Be very careful when calling -[NSNotificationCenter removeObserver:]

rentzsch:

Samuel Défago:

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];

March 3, 2011 at 11:17am
3 notes

UIEdgeInsets

A common task for UIKit devs: Inset a CGRect by an amount from each edge:

UIEdgeInsetsInsetRect(rect,insets);

And to do that in one line of course the constructor of UIEdgeInsets

UIEdgeInsetsMake(top,left,bottom,right);

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.

UIEdgeInsetsInsetRect(CGRectMake(0,0,100,100), UIEdgeInsetsMake(10,10,-50,50));

results in the following CGRect: {10,10,140,40}

August 4, 2010 at 5:12pm
0 notes

Great Summary of the Fast App Switching iOS4 livecycle of an App →

July 27, 2010 at 9:34am
41 notes

The hidden complexity of iOS 4 Multitasking

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!

Update:

  • 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.

Update 2:

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.

    May 10, 2010 at 5:01pm
    0 notes

    Separation of Tasks

    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!

    April 20, 2010 at 9:28am
    2 notes

    great setup and extension tips for mercurial →

    April 3, 2010 at 12:21pm
    1 note

    NSString stringWithFormat: - indexed arguments!

    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";
    [NSString stringWithFormat:formatString,5,120];
    // 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";
    [NSString stringWithFormat:formatString,5,120];
    // 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";
    [NSString stringWithFormat:formatString,5,120];
    // 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.

    March 29, 2010 at 6:00pm
    0 notes

    Choosing the right spot for your task

    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 NSText or 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 NSWindowController, -awakeFromNib might get called twice, once for the nib it lives in, once for the nib it itself is loading. Use -windowWillLoad and -windowDidLoad instead. If you are an NSView the -viewWillMoveToSuperview:, -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.

    March 25, 2010 at 8:00pm
    0 notes

    Handy constants

    Another element that is easily overlooked, but can make code and life so much easier: Convenience Constants. Take some examples:

  • CGPointZero
  • CGRectZero
  • CGSizeZero
  • CGRectInfinite - mostly used in Core Image contexts
  • CGRectNull - not equal to CGRectZero, returned by e.g. CGRectIntersection()
  • NSNotFound
  • CGAffineTransformIdentity
  • UIEdgeInsetsZero
  • UITrackingRunLoopMode
  • NSFoundationVersionNumber
  • NSRunLoopCommonModes
  • NSEventTrackingRunLoopMode
  • 4:23pm
    0 notes

    Cocoa is object oriented, right?

    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:

  • UIImagePNGRepresentation(image)
  • UIImageJPEGRepresentation(image,quality)
  • UIImageWriteToSavedPhotosAlbum(image,target,selector,context)
  • UIInterfaceOrientationIsLandscape(orientation)
  • UIInterfaceOrientationIsPortrait(orientation)
  • CGRectIntegral(rect)
  • CGRectInset(rect,dx,dy)
  • CGRectOffset(rect,dx,dy)
  • CGRectStandardize(rect)
  • CGRectDivide(rect,*slice,*remainder,amount,edge)
  • CGRectGetMidX(rect)
  • CGRectGetMidY(rect)
  • MIN(A,B)
  • MAX(A,B)
  • NSStringFromSelector(aSelector)
  • Look them up, they might prove useful, or just make your code easier to read!

    March 24, 2010 at 6:57pm
    0 notes

    Occam’s Razor

    In debugging a Problem always, I repeat, always apply Occam’s Razor and look for the simplest solution out there. 9 out of 10 times it is the blatant obvious errors you are just attributing to some higher power, deeper edge case or otherwise dark wizardry.

    Our brains are wired this way, we see connections where there are none, we associate. I’ve seen it time over time, and experienced it myself quite a lot. You begin making totally bogus assumptions about what might go on if you call the @synthesized setter while writing your own custom getter, when all there was was an over-autorelease earlier. You come up with specialized theories over the process involved when nib-files are instantiated, when all that is causing the error is a standard c = vs == typo.

    If you encounter a problem, take a deep breath. Relax, and then go looking for the OBVIOUS errors. If you don’t see them right away, explain the situation to someone and let him or her look. Only after that begin wondering of to your dream land of wildy uncalled for assumptions.

    March 17, 2010 at 9:25am
    0 notes

    +initialize

    When implementing a Cocoa Classes +initialize method, don’t forget to check if you are you, otherwise you might get called twice!

    @implementation MyCustomClass
    +(void)initialize {
     if (self == [MyCustomClass class]) {
       // do your stuff here!
      }
    }

    March 10, 2010 at 8:41am
    0 notes

    Retain, Release, Autorelease, etc.

    Something I tend to do, which saved my but quite some time. Interestingly enough this is somewhat discouraged in iPhone OS due to the overhead of autoreleases and the relatively small power of the platform.

    Instead of:

    SomeClass *myInstance = [[SomeClass alloc] init];
    // do something with myInstance
    [myInstance release]; // release it again

    I do:

    SomeClass *myInstance = [[[SomeClass alloc] init] autorelease];
    // do something with myInstance

    Straight away. That really helps me avoid complexity and stupid releasing bugs caused by the “// do something with myInstance” block having an early return, branches, or other issues.