property的系統實現分析

weixin_34007291發表於2017-12-17

我們在使用@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

相關文章