1. 程式人生 > >Objective-C之property屬性分析

Objective-C之property屬性分析

@property和@synthesize用來生成屬性的set和get方法

格式:
@property(屬性列表) 型別 屬性名
@synthesize 屬性名

屬性列表包括:strong(retain), copy, weak(assign), atomic, nonatomic, readonly, readwrite, getter=name, setter=name
strong和weak是引入ARC時加入的關鍵字
屬性列表與關鍵字的對應關係

屬性值

關鍵字

所有權

strong, copy, retain

__strong

weak

__weak

assign, unsafe_unretained

__unsafe_unretained

屬性預設為assign(如果屬性是NSObject(或者它子類的)的物件時,預設為strong), readwrite, atomic
@property int i;等價於@property(assign, readwrite, atomic) int i;
@property NSString *s;等價於@property(strong, readwrite, atomic) NSString *s;

strong和retain類似,對所指的物件引用計數加1(如果所指物件是可變物件的話)
NSMutableString *str = [[NSMutableString alloc] initWithUTF8String:"mutablestring"];
member.strong = str; // 標頭檔案定義@property(strong) NSString *strong; strong和str指向同一個記憶體地址
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)(str))); // 輸出2(NSLog可能對引用計數結果有影響,所以在這之前不要使用類似NSLog(@"%@", member.strong);語句,最好在檢視引用計數時先遮蔽掉其他NSLog)
NSLog(@"%@", member.strong); // 輸出mutablestring
[str deleteCharactersInRange:NSMakeRange(0, 7)];
NSLog(@"%@", member.strong); // 輸出string

我改變的是str的內容,但是strong卻被同時改變了,這是因為strong和str指向的是同一塊記憶體(可以在Xcode中除錯View Memory of "*_strong"和View Memory of "*str"可以看到它們的Address是一致的),該記憶體的引用計數為2,所以改變s的內容實際上就改變了strong的內容

另外如果用一個strong指標來指向不可變物件str,那麼str的引用計數不會加1

NSString *str = @"string"; // @"string"存放在記憶體的常量區(不可變),生命週期為整個程式的生命週期,引用計數為一個很大的數,不會被改變
NSString *__strong s = str; // 試圖使str的引用計數加1
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)(str))); // 引用計數不變

那麼如果我們不想讓strong和str指向一個地方,這時copy就派上用場了,copy和strong類似,但是它不影響所指向的物件的引用計數,如果str指向的物件是可變的,那麼copy會建立一個所指向物件的不可變副本(你不能修改copy指向的地址的內容,即使這個copy是NSMultableString,[str copy]同理),它們不指向同一個記憶體地址

NSMutableString *str = [[NSMutableString alloc] initWithUTF8String:"mutablestring"]; // 堆中分配記憶體
member.cp = str; // 標頭檔案定義@property(copy) NSString *cp; cp指向str的副本,cp和str指向不同地址
NSLog(@"%@", member.cp); // 輸出mutablestring
[str deleteCharactersInRange:NSMakeRange(0, 7)];
NSLog(@"%@", member.cp); // 然並卵,cp沒有改變,輸出mutablestring
NSLog(@"%p %p", str, member.cp); // 地址不同

member.mscp = str; // 標頭檔案定義@property(copy) NSMutableString *mscp
NSLog(@"%p %p", str, member.mscp); // 地址不同
[member.mscp deleteCharactersInRange:NSMakeRange(0, 7)]; // mscp指向不可變的副本,即使它是NSMutableString,也不能修改其指向的內容。執行時報錯Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with deleteCharactersInRange:'

如果str指向的物件不可變,結果又會不一樣

NSString *str = [[NSString alloc] initWithFormat:@"string"]; // str雖然在堆中分配記憶體,但是@"string"實際上是不能被改變的,這個物件會被新增到自動釋放池
member.cp = str;
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)(str))); // 引用計數為一個很大值
NSLog(@"%p %p", str, member.cp); // cp並沒有分配新的空間,因為str指向的記憶體地址不可改變(也就是說@"string"這個值無法改變,不能變成@"abc"之類的),所以cp和str指向了同一個地址
NSString *str = @"string"; // @"string"存放在記憶體的常量區(不可變),生命週期為整個程式的生命週期,引用計數為一個很大的數,不會被改變
member.cp = str;
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)(str))); // 一個非常大的引用計數
NSLog(@"%p %p", str, member.cp); // cp並沒有分配新的空間,因為str指向的記憶體地址不可改變(也就是說@"string"這個值無法改變,不能變成@"abc"之類的),所以cp和str指向了同一個地址

weak和assign不會對所指的物件引用計數加1(還是指向的同一個記憶體地址),它們的區別就是當weak指向的記憶體區域被釋放時,weak指標會被賦值為nil,而如果用的是assign,不會被賦值為nil,再去使用這個指標時,執行時會報錯
NSString *str= [[NSString alloc] initWithFormat:@"%s", "Name:zyu"];
member.weak = str; // 標頭檔案定義@property(weak) NSString *weak; weak和str指向同一個記憶體地址
str= nil; // str空間釋放時,weak被賦值成nil
NSLog(@"%@", member.weak); // 輸出(null)
等價於
NSString *str = [[NSString alloc] initWithFormat:@"%s", "Name:zyu"];
NSString * __weak _weak = str; // 還可以用關鍵字來定義weak屬性
str = nil;
NSLog(@"%@", _weak); // 輸出(null)
@當我把"Name:zyu"改成"zyu"時,NSLog輸出的結果不是(null),不知道是不是Xcode的bug。( ̄▽ ̄)
NSString *str= [[NSString alloc] initWithFormat:@"%s", "Name:zyu"];
member.assign= str; // 標頭檔案定義@property(assign) NSString *assign; assign和str指向同一個記憶體地址
str= nil; // str空間釋放時,assign不會被賦值成nil
NSLog(@"%@", member.assign); // 執行時報錯EXC_BAD_ACCESS,有時候又不報錯,沒有任何輸出(╯°□°)╯︵ ┻━┻

Objective-C中的@property:http://www.devtalking.com/articles/you-should-to-know-property/
Transitioning to ARC Release Notes:https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
Objective-c的@property 詳解:http://www.cnblogs.com/andyque/archive/2011/08/03/2125728.html