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
)?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With