Why can a weak member be nulled in an instance method before the method is finished?

只愿长相守 提交于 2020-01-16 19:15:34

问题


And why can it vary between Debug/Release/Simulator/Device combinations? (Sample code below.)

I inherited some code that "works" in the simulator and on devices in Debug, but not on a device in Release (used to with last LLVM). It's related to ARC and a weak property (which should have been strong), but I don't understand fully what's happening and am hoping someone can explain it to me and why it varies between Debug/Release/Device/Simulator.

I created a simple sample project that demonstrates this, with all code in AppDelegate.m as included below. When run in the simulator, the logged output is:

-[BaseThingMaker baseMakeThing]: returning a Thing
-[ThingMaker makeThing]: returning a Thing
-[AppDelegate application:didFinishLaunchingWithOptions:]: got a Thing

When run on the device, the logged output is:

-[BaseThingMaker baseMakeThing]: returning a Thing
-[ThingMaker makeThing]: returning a (null)
-[AppDelegate application:didFinishLaunchingWithOptions:]: got a (null)

On a device (in Debug or Release), the weak self.thing in ThingMaker is null even for the log message before the method is complete. On the simulator, the self.thing object is returned.

Sample code from Single Window App (AppDelegate.m):

#import "AppDelegate.h"

@interface Thing : NSObject
@end
@implementation Thing
@end

@interface BaseThingMaker : NSObject
-(Thing *)baseMakeThing;
@end

@implementation BaseThingMaker
-(Thing *)baseMakeThing
{
    Thing *thing = [Thing new];
    NSLog(@"%s: returning a %@", __PRETTY_FUNCTION__, NSStringFromClass([thing class]));
    return thing;
}
@end


@interface ThingMaker : NSObject
- (id)makeThing;
@end

@interface ThingMaker ()
@property (weak, nonatomic) id thing;
@end

@implementation ThingMaker
- (id)makeThing
{
    BaseThingMaker *baseThingMaker = [BaseThingMaker new];
    self.thing = [baseThingMaker baseMakeThing];
    NSLog(@"%s: returning a %@", __PRETTY_FUNCTION__, NSStringFromClass([self.thing class]));
    return self.thing;
}
@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ThingMaker *thingMaker = [ThingMaker new];
    id thingIWant = [thingMaker makeThing];
    NSLog(@"%s: got a %@", __PRETTY_FUNCTION__, NSStringFromClass([thingIWant class]));
    return YES;
}
@end

回答1:


The method baseMakeThing does not return an object its call owns, in this case it returns an autoreleased object. If it is an autoreleased object it will have an owner - the autorelease pool - until the pool it emptied when the run loop cycles around. So the assignment to the weak property will be valid until that happens, and the code will appear to work...

Unless the compiler optimises, instead of retaining an autoreleased object the compiler can sometimes pull the object out of the autorelease pool and take ownership that way. If the compiler does this then immediately after the assignment to the weak property the compiler can relinquish ownership, as the reference is not used later in makeThing, and this will in turn null the weak property...

The differences you are seeing are down to how much optimisation the compiler is doing depending on the type of build.



来源:https://stackoverflow.com/questions/23960634/why-can-a-weak-member-be-nulled-in-an-instance-method-before-the-method-is-finis

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!