Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 555
Mircea Ispas Avatar asked Dec 07 '22 03:12

Mircea Ispas


2 Answers

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.

like image 167
Wil Shipley Avatar answered Dec 31 '22 03:12

Wil Shipley


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.

like image 29
Vincent Gable Avatar answered Dec 31 '22 03:12

Vincent Gable