Consider the following code:
@interface ClassA : NSObject
@property (nonatomic, copy) void(^blockCopy)();
@end
@implementation ClassA
@synthesize blockCopy;
- (void)giveBlock:(void(^)())inBlock {
blockCopy = inBlock;
}
@end
Then use it in a class which has a strong
property of type ClassA
called someA
:
self.someA = [[ClassA alloc] init];
[self.someA giveBlock:^{
NSLog(@"self = %@", self);
}];
dispatch_async(dispatch_get_main_queue(), ^{
self.someA.blockCopy();
self.someA = nil;
});
If I run that built O3
with ARC enabled, on iOS, it crashes during the self.someA.blockCopy();
call inside objc_retain
. Why?
Now I realise that people are probably going to say I should be setting it with self.blockCopy = inBlock
but I did kind of think that ARC should be doing the right thing here. If I look at the assembly (ARMv7) produced from the giveBlock:
method it looks like this:
.align 2
.code 16
.thumb_func "-[ClassA giveBlock:]"
"-[ClassA giveBlock:]":
push {r7, lr}
movw r1, :lower16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4))
mov r7, sp
movt r1, :upper16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4))
LPC0_0:
add r1, pc
ldr r1, [r1]
add r0, r1
mov r1, r2
blx _objc_storeStrong
pop {r7, pc}
That is calling objc_storeStrong
which in turn does a retain
on the block and a release
on the old block. My guess is that ARC is not properly noticing it's a block property as I think it should be calling objc_retainBlock
instead of the normal objc_retain
.
Or, am I just totally wrong and actually ARC is doing what it documents and I've just read it the wrong way?
Discussion very much welcome on this - I find this to be rather intriguing.
Points to note:
- It doesn't crash on OS X.
- It doesn't crash built
O0
.