我们在使用@property
这个快捷定义的时候,都没怎么注意系统是怎么实现的,那么我们来看看系统实现的,和我们自己写的setter方法是否一致呢?
Strong
我们来分析一下最简单的strong类型的实现。
@property (nonatomic, strong) NSString *str;
如果是我们自己写setter方法大概是:
- (void)setStr:(NSString *)str {
_str = str;
}
如果是MRC的话,可能是这样的:
- (void)setStr:(NSString *)str {
if (_str != str) {
[_str release];
_str = [str retain];
}
}
当然系统的方法是经过优化的,直接调用的C方法,所以这里不再考虑MRC的写法,直接来对比ARC的结果。
store strong
首先我们来看下objc是如何实现strong的,根据objc的源码可以知道,强引用是通过一个objc_storeStrong
的方法来实现的。其实现入下:
void objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
这和我们上面MRC的写法类似,只不过是通过C方法来实现了retain和release。
转换为C语言
大家都知道objc的方法其实都是通过send message的方式转换为C语言调用的,所以一个基本的setter方法的最终结果应该是这样的:
void setter(id self, SEL selector, NSString *str);
具体实现
根据反汇编结果,其中系统默认的实现汇编如下:
sub sp, sp, #0x30 ; 申请栈空间
stp x29, x30, [sp, #0x20]
add x29, sp, #0x20
adrp x8, 17
add x8, x8, #0xfc0
stur x0, [x29, #-0x8]
str x1, [sp, #0x10]
str x2, [sp, #0x8]
ldr x0, [sp, #0x8]
ldur x1, [x29, #-0x8] ; x1 = self
ldrsw x8, [x8] ; 偏移量
add x8, x1, x8 ; x8 = self + 偏移量
str x0, [sp]
mov x0, x8 ; x0 = x8
ldr x1, [sp] ; x1 = str
bl 0x1000bcad8 ; objc_storeStrong(self + 偏移量, str)
ldp x29, x30, [sp, #0x20]
add sp, sp, #0x30 ; 退栈
ret
翻译为C语言大概就是这样的:
void setStr(id self, SEL sel, NSString *str) {
objc_storeStrong(self + delta, str);
}
而我们自定义的setter方法
- (void)setStr:(NSString *)str {
_str = str;
}
的反汇编结果却比系统的结果多出一些函数调用
sub sp, sp, #0x30 ; =0x30
stp x29, x30, [sp, #0x20]
add x29, sp, #0x20 ; =0x20
add x8, sp, #0x8 ; =0x8
mov x9, #0x0 ;
stur x0, [x29, #-0x8]
str x1, [sp, #0x10]
str x9, [sp, #0x8]
mov x0, x8
mov x1, x2
bl 0x104d14ad8 ; symbol stub for: objc_storeStrong
adrp x8, 17
add x8, x8, #0xfc8 ; =0xfc8
ldr x9, [sp, #0x8]
ldur x0, [x29, #-0x8]
ldrsw x8, [x8]
add x8, x0, x8
mov x0, x8
mov x1, x9
bl 0x104d14ad8 ; symbol stub for: objc_storeStrong
mov x8, #0x0
add x9, sp, #0x8 ; =0x8
mov x0, x9
mov x1, x8
bl 0x104d14ad8 ; symbol stub for: objc_storeStrong
ldp x29, x30, [sp, #0x20]
add sp, sp, #0x30 ; =0x30
ret
翻译成C语言大概是这样的:
void setStr(id self, SEL sel, NSString *str) {
id tmp = nil;
objc_storeStrong(&tmp, str);
objc_storeStrong(self + delta, tmp);
objc_storeString(&tmp, nil);
}
可以看到这里多出一个临时变量,而且即使编译优化为-os
,也是同样的结果。这里不清楚为什么会产生一次临时变量,从结果上来看,是不如系统的默认行为的。
Weak
在objc的源码中,weak是通过objc_storeWeak
来实现的,这个方法的实现比较复杂,涉及到了全局的弱引用表,这里就不作介绍了。
同样,我们来对比下系统默认实现的weak类型,和我们重写的setter方法,从结果上来看也是产生了一个strong类型的临时变量。
sub sp, sp, #0x40 ; =0x40
stp x29, x30, [sp, #0x30]
add x29, sp, #0x30 ; =0x30
adrp x8, 17
add x8, x8, #0xfd0 ; =0xfd0
stur x0, [x29, #-0x8]
stur x1, [x29, #-0x10]
str x2, [sp, #0x18]
ldr x0, [sp, #0x18]
ldur x1, [x29, #-0x8]
ldrsw x8, [x8]
add x8, x1, x8
str x0, [sp, #0x10]
mov x0, x8
ldr x1, [sp, #0x10]
bl 0x104f38ae4 ; symbol stub for: objc_storeWeak
str x0, [sp, #0x8]
ldp x29, x30, [sp, #0x30]
add sp, sp, #0x40 ; =0x40
ret
sub sp, sp, #0x30 ; =0x30
stp x29, x30, [sp, #0x20]
add x29, sp, #0x20 ; =0x20
add x8, sp, #0x8 ; =0x8
mov x9, #0x0
stur x0, [x29, #-0x8]
str x1, [sp, #0x10]
str x9, [sp, #0x8]
mov x0, x8
mov x1, x2
bl 0x1044d8ad8 ; symbol stub for: objc_storeStrong
adrp x8, 17
add x8, x8, #0xfc8 ; =0xfc8
ldr x9, [sp, #0x8]
ldur x0, [x29, #-0x8]
ldrsw x8, [x8]
add x8, x0, x8
mov x0, x8
mov x1, x9
bl 0x1044d8ae4 ; symbol stub for: objc_storeWeak
mov x8, #0x0
add x9, sp, #0x8 ; =0x8
str x0, [sp]
mov x0, x9
mov x1, x8
bl 0x1044d8ad8 ; symbol stub for: objc_storeStrong
ldp x29, x30, [sp, #0x20]
add sp, sp, #0x30 ; =0x30
ret