views:

196

answers:

3

Pretty much everyone that writes about the UISplitView on the iPad uses the following code structure to dismiss a popover:

if (popoverController != nil) {
    [popoverController dismissPopoverAnimated:YES];
}

I though Objective-C was happy to ignore messages that are passed to nil? In fact, in the File > New Project > New Split View Application template, there's an example of this shortcut in the same code block (DetailsViewController.m):

- (void)setDetailItem:(id)newDetailItem { 
    if (detailItem != newDetailItem) {
        [detailItem release];                             //might be nil
        detailItem = [newDetailItem retain]; 

        // Update the view.
        [self configureView];
    }


    if (popoverController != nil) {
        [popoverController dismissPopoverAnimated:YES];   //was checked for nil
    }        
}

Why is that second if necessary?

+1  A: 

In a nutshell, it's not. I guess some people like to be explicit...?

Dave DeLong
+3  A: 

It isn't necessary. File a bug.

bbum
I don't think it deserves a _bug report_.
zneak
The existence of this question is proof positive that there that the inconsistency has a related (and unnecessary) expense. Minor? Absolutely. But... well... death by 1000 cuts and all.
bbum
+4  A: 

In this case, it's not important and just adds a line of code.

However, when the return type of a method is not an integral type, this check can be important. Oh crap, it seems they fixed that in ObjC 2.0.

It's important to check for nil when a non-scalar type should be returned. Take this example:

struct complex_t
{
    int foo, bar, frob;
    double nicate;
};

@interface Foo : NSObject {}
-(struct complex_t)complex;
@end

@implementation Foo
-(struct complex_t)complex { return (struct complex_t){-1, 2, -1, 1e14}; }
@end

int main()
{
    struct complex_t c;
    memset(&c, 0xFFFFFFFF, sizeof c);
    c = [nil complex];
    printf("%i %i %i %g\n", c.foo, c.bar, c.frob, c.nicate);
}

In this example, our c is happily memset to have -1s in every field (except for the double, which I don't quite know what it does). Messaging nil indeed resets everything to zero.

But wait!

Just suppose we change our main a little bit:

int main()
{
    struct complex_t c;
    memset(&c, 0xFFFFFFFF, sizeof c);
    [[[Foo alloc] init] complex]; // NEW LINE HERE!
    c = [nil complex];
    printf("%i %i %i %g\n", c.foo, c.bar, c.frob, c.nicate);
}

It now happens that c will hold what [[[Foo alloc] init] complex] returned, even though the return value was technically not used. (EDIT Compiled from gcc -lobjc -framework Cocoa as an x86_64 binary. Your mileage may vary with your architecture.)

It seems the return value of a big struct when messaging nil is undefined.

zneak
+1 sending a message to `nil` and expecting a struct as a response can result in garbage. In this case, checking for `nil` is a good idea.
Dave DeLong
The return value isn't used.
bbum
@bbum: I know. It's the first thing I wrote: _In this case, it's not important and it just adds a line of code._ It's just you shouldn't think that it's always, systematically fine to message `nil` without checking.
zneak
Bonus point for such a thorough answer! You are absolutely correct that it is important to know when it isn't safe. Usually, I draw the line at "is the return value used or not". If so, always test and always show that you have thought the nil case through and have explicitly picked a value.
bbum
@bbum: I completely agree.
zneak
This is exactly what I thought, so you must be right :D
Rob Fonseca-Ensor