Objective C chaining calls

I'm new to Objective C so please forgive me if this question seems stupid.

First sample:

int main(int argc, const char* argv[])
{
    @autoreleasepool
    {
        MyCoolDelegate* myCoolDelegate = [[MyCoolDelegate alloc] init];
        [[NSApplication sharedApplication] setDelegate: myCoolDelegate];

        return NSApplicationMain(argc, argv);
    }
}

Second sample:

int main(int argc, const char* argv[])
{
    @autoreleasepool
    {
        [[NSApplication sharedApplication] setDelegate: [[MyCoolDelegate alloc] init]];

        return NSApplicationMain(argc, argv);
    }
}

As C++ programmer I expect both main functions should have the same behaviour, but second main crashes on return NSApplicationMain(argc, argv); while first one sets the delegate and works as expected.

Can you please explain what is the difference between these samples? Is there some black magic around temporary objects in Objective C(I assume [MyCoolDelegate alloc] init] will return a temporary object of type MyCoolDelegate)?

Answers


You probably want to make myCoolDelegate an instance variable, static variable, or create it in a xib file (like the Xcode template you get from File > New Project… > Cocoa Application does).

Actually, both examples are technically wrong. One "accidentally works" because the compiler isn't (currently!) performing the legal optimization to release myCoolDelegate as soon as it's used for the last time, which is before the NSApplicationMain call.

Per the spec, "By default, local variables of automatic storage duration do not have precise lifetime semantics. Such objects are simply strong references which hold values of retainable object pointer type, and these values are still fully subject to the optimizations on values under local control."

Usually -setDelegate: methods don't retain/strongly-reference things, to prevent strong reference cycles, where two objects both keep each other from being deallocated. For compatibility, NSApplication uses an __unsafe_unretained reference to it's delegate instead of a (usually preferable) __weak reference (this might change in future OS X versions course). So when NSApplication tries to talk to it's delegate after it has been deallocated, you get a crash. The delegate can be deallocated here because nothing is strongly referencing it after the -setDelegate: call.

In summary: using an implicit temporary variable, or an explicit local variable only keep an object alive until the variable's last use. They do not guarantee that the object is kept alive as long as the variable is in scope. It's unfortunate that the compiler isn't being as aggressive as it could be, which makes an explicit local variable appear to extend an object's life time more than it's guaranteed to. You might see different behavior if you turned on more optimizations. You need to make myCoolDelegate a "longer lived" kind of variable (ivar, staic, or global) or use the objc_precise_lifetime attribute when declaring it.

__attribute__((objc_precise_lifetime)) MyCoolDelegate* myCoolDelegate = [[MyCoolDelegate alloc] init];

This situation is complicated, but it doesn't come up in practice much, because things that are delegates of another object are almost always referenced through an ivar, or something "long lived", not just a local variable.


To elaborate on @tenfour’s answer, this line:

MyCoolDelegate *myCoolDelegate = [[MyCoolDelegate alloc] init];

Has an implicit “__strong” in it, so it really means this:

__strong MyCoolDelegate *myCoolDelegate = [[MyCoolDelegate alloc] init];

If you change your first line to something like this:

__unsafe_unretained MyCoolDelegate *myCoolDelegate = [[MyCoolDelegate alloc] init];

Then you should see the same (bad) behavior in both cases.


In your first example, myCoolDelegate holds a reference to the object, keeping it alive until myCoolDelegate goes out of scope at the end of your @autoreleasepool block where it is automatically released deallocated.

In your second example, the MyCoolDelegate object you create is not retained by any local variable. You are creating a temporary which has a retain count only for the duration of the statement.

If setDelegate retains the object, the object will continue to live with a retain count of 1, and your app won't crash when the application tries to use it.

If on the other hand setDelegate only keeps a weak reference, then your app will crash because its retain count is 0 after calling setDelegate returns, so the object is deallocated before NSApplicationMain.


Need Your Help

Database model for favoriting items.

ruby-on-rails ruby ruby-on-rails-3 database-design activerecord

I have a Rails app with users, suggestions and searches. There is a many-to-many association between users and searches such that each user can be in many searches and each search can have many use...