天天看點

深入了解@property的關鍵字:weak,strong,copy,assign,unsafe_unretained

關于屬性這些關鍵字的文章,相信大家已經看過不少了,不過都是千篇一律的東西,并且其中有很多的錯誤。那麼今天我們就來讨論讨論。

首先我們還是來介紹一下這些關鍵字的作用吧。

我認為:weak,strong,copy,assign,unsafe_unretained這些關鍵字其實就是這個屬性的set語義。

weak:既不釋放舊值,也不保留新值。當它指向的内容被釋放的時候,它本身也會被置空。好處就是可以防止野指針通路。

weak的實作原理:系統通過一個CFMutableDictionary的hash表來管理weak指針。一個對象可能有多個weak指針指向,當這個對象被釋放的時候,就會從hash表中查找所有指向這個對象的weak指針,并把它們都置為nil。

strong:釋放舊值,并保留新值。它會對指向的内容進行引用計數+1的操作。

copy:釋放舊值,并拷貝新值。它也會對指向的内容進行引用計數+1的操作。

assign:就是一個簡單的指派操作。它和weak很像,不會增加引用計數,但是當它指向的内容被釋放的時候,它不會被置空,是以它還指向那個内容,是以容易造成野指針通路。一般用來修飾基本資料類型。當然指向對象也可以的。

unsafe_unretained:可以了解為MRC下的weak。和assign類似,同樣不會自動置空,隻能用來修飾對象類型。容易造成野指針通路。

首先聲明一下,他對于這些關鍵字的解釋是沒有問題的,但是舉的例子有問題。

po一下網址:http://www.cnblogs.com/langtianya/p/3691035.html

這是我随便百度出來的。

PS:想要有深入的了解,必須掌握好記憶體的相關知識。

首先,我們按照他說的,寫個代碼,為了友善,我把位址也列印出來:

@interface ViewController ()

@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.string1 = @"hahaha";
    self.string2 = self.string1;
    NSLog(@"Str1 point to Address: %p", self.string1);
    NSLog(@"Str2 point to Address: %p", self.string2);

    self.string1 = nil;
    NSLog(@"String 1 = %@", self.string1);
    NSLog(@"String 2 = %@", self.string2);
    NSLog(@"Str1 point to Address: %p", self.string1);
    NSLog(@"Str2 point to Address: %p", self.string2);
}

@end
           
深入了解@property的關鍵字:weak,strong,copy,assign,unsafe_unretained

這是網站上說的,結果是空???你信麼?是不是大家都覺得結果肯定是空?我運作一下。。。

深入了解@property的關鍵字:weak,strong,copy,assign,unsafe_unretained

嘿~有意思了吧~并不是空!!!而且這裡你改成什麼都是一樣的,比如,改成如下的:

self.string1 = @"hahaha";
self.string2 = self.string1;
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);

self.string1 = @"hehehe";
NSLog(@"String 1 = %@", self.string1);
NSLog(@"String 2 = %@", self.string2);
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);
           

結果毫無疑問,還是String 2 = hahaha

為什麼?學過MRC的同學肯定知道這是怎麼回事了。其實很好解釋,一開始,string1和string2都指向同一個位址,但之後,string1已經不再指向這個位址了,而是指向了其他位址。也就是說,一開始string1指向的那個位址對應的值并沒有改變,而string2正是指向的這個位址。

舉個例子,string1牽着一隻狗(self.string1 = @”hahaha”),此時string2也牽着這隻狗(self.string2 = self.string1),現在這隻狗有兩個繩子牽着。

情景1:突然,string1的繩子斷了(self.string1 = nil),但是string2的繩子并沒有斷啊。

情景2:突然string1不喜歡這隻狗了,去牽了另一隻狗(self.string1 = @”hehehe”),但是此時string2還是牽着以前的那隻。

這麼說就很好了解了吧。關鍵就是這一句:self.string2 = self.string1

如果換成C語言的基本資料類型,可能會更好了解一些:

int a = ;
int b = a;
a = ;
printf("%d", b);
           

你們說,輸出的b的結果是多少?毫無疑問是10啊!

是以說,上面的self.string2 = self.string1做的就是這樣的事情。

到此,可能大家已經明白了。在這個屬性中,無論你string2用的是weak/strong/copy/assign,結果都是一模一樣的。因為被改變的不是string1指向的内容,而是string1本身的指向。

是以想要真正去測試那些屬性的語義,必須有一個正确的方法:要被改變的是内容,而不是指向。

定義一個Person類:

@interface Person : NSObject <NSCopying>

//這裡用strong或copy都可以的,原因同上
@property (nonatomic, copy) NSString *name;

@end
           

實作Copy方法。重寫description方法,讓它輸出name的值

- (NSString *)description {
    return self.name;
}

- (id)copyWithZone:(NSZone *)zone {
    Person *newPer = [[self class] allocWithZone:zone];
    newPer.name = self.name;
    return newPer;
}
           

接下來我們開始測試了~

@interface ViewController ()

@property (nonatomic, copy/strong/weak/assign/unsafe_unretained) Person *per2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

        Person *per1 = [[Person alloc] init];
    per1.name = @"Tom";

    self.per2 = per1;

    //情景1
    per1.name = @"Jerry";
    //情景2
    per1.name = nil;
    //情景3
    per1 = nil;
    //情景4
    per1 = [[Person alloc] init];
    per1.name = @"NewName";

    NSLog(@"per1 - %@", per1);

    //PS:專門把per2的log寫在外面,就是考慮到作用域的問題。也就是說,出了下面這個大括号,per1有可能會被釋放。就看用什麼關鍵字來修飾per2了
    [self logResult];
}

- (void)logResult {
    NSLog(@"per2 - %@", self.per2);
}

@end
           

上面的情景4在上文已經介紹過了,情景1和情景2都屬于改變了指向的内容,情景3和情景4都是改變了自己本身的指向。

對于weak

情景1:兩個都是 Jerry

情景2:兩個都是 空

情景3:兩個都是 空

情景4:per1為 NewName,per2為 空

對于strong

情景1:兩個都是 Jerry

情景2:兩個值都是 空

情景3:per1為 空,per2為 Tom

情景4:per1為 NewName,per2為 Tom

對于copy

為了實作copy,需要遵守NSCopying協定,實作-copyWithZone方法。

情景1:per1為 Jerry,per2為 Tom

情景2:per1為 空,per2為 Tom

情景3:per1為 空,per2為 Tom

情景4:per1為 NewName,per2為 Tom

對于assign

情景1:兩個都是 Jerry

情景2:兩個都是 空

情景3:per1為 空,per2無法輸出,出現野指針錯誤。

情景4:per1為 NewName,per2無法輸出,出現野指針錯誤。

對于unsafe_unretained

情景1:兩個都是 Jerry

情景2:兩個都是 空

情景3:per1為 空,per2無法輸出,出現野指針錯誤。

情景4:per1為 NewName,per2無法輸出,出現野指針錯誤。

繼續閱讀