1
votes

I've got a question regarding the following code:

@interface ViewController ()
@property (nonatomic, weak) CALayer *someLayer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.someLayer = [CALayer layer];
    self.someLayer.frame = CGRectMake(10.0, 10.0, 100.0, 100.0);
    self.someLayer.backgroundColor = [UIColor yellowColor].CGColor;
    [self.view.layer addSublayer:self.someLayer];
}
@end

From my understanding [CALayer layer] should return an autoreleased value, which should live as long as the method call is in progress. It is then referenced by the weak property someLayer. Since the layer is later retained by the view's layer everything should be fine and the layer should appear. What it does, but only on devices < iPhone 5S. At least when I run the code in the simulators.

On newer devices the layer won't be displayed. Changing the property attribute to strong solves the problem, but I'd like to know why the layer gets released immediately.

Do you have any hints how I can debug this behaviour to see what has changed?

Thanks!

Update:

Using self.someLayer = [CALayer [alloc]] init] gives an appropriate warning Xcode warning

which I understand because when using alloc the value is owned by the caller. Since the owner does not retain the value there's no strong reference. An convenience initializer like layer should autorelease an retained value, which should be accessible until the autorelease pool is drained. Also there's no warning by Xcode in this case.

Update #2: It gets more interesting... I tested the code on an iPhone 6 with iOS 8 and on an iPhone 4 with iOS 6. The layer won't be displayed on neither of them...

BUT

if I set a breakpoint at the line of the creation and step over it, the layer will be visible on simulators < iPhone 5S and on the devices.

enter image description here

How could I check what's going on under the hood?

Update #3 and some more explanation:

There is a great article on Big Nerd Ranch about this behaviour ARC Gotcha - Unexpectedly Short Lifetimes

A look at the disassembly for the code above shows how ARC inserts _objc_retainAutoreleasedReturnValue

From the article:

you can see ARC's "avoid the autorelease pool" optimization is being used: if a method returns an autoreleased object, and the caller doesn't otherwise need to hang on to it, ARC can avoid the trip to the autorelease pool. The method will return a retained object and objc_retainAutoreleasedReturnValue will dispose of it.

So, to avoid the problem, either declare a strong property (as I mentioned above) or have a look at @graver's answer.

2
ARC is on? It is weird, maybe you should debug on device. I dont trust simulators.Yucel Bayram

2 Answers

2
votes

Since your someLayer property is weak there's nothing to hold a strong reference to your layer. You should change your code like this:

...
CALayer *layer = [CALayer layer]; // by default layer this is __strong, so layer holds a strong reference until the end of the scope
[self.view.layer addSublayer:layer]; // Now self.view.layer retains the layer
self.somelayer = layer; // weak reference it

self.someLayer.frame = CGRectMake(10.0, 10.0, 100.0, 100.0);
self.someLayer.backgroundColor = [UIColor yellowColor].CGColor;

// In the end of the scope layer would be released, but it's also retained by self.view.layer and weak referenced by self.someLayer
...
1
votes

@graver 's code works for me on a real iPhone 6 device, which means layer is showed, you should always rely on real devices instead of simulators. You can refer to this question and xcode debugger hide issue with weak proper, it seems that the debugger would hold a strong refrence to the autorelease object, so it is kept and it is showed.

You can always test self.somelayer by logging it out.

self.someLayer = [CALayer layer];
NSLog(@"someLayer is %@",_someLayer);

It should log as Null, but on some simulators it would has value, which shouldn't happen.