MRC
1. assign
MRC 下 assign 為屬性的默認修飾符,無論是簡單的數(shù)據(jù)類型,還是指向?qū)ο蟮闹羔槨?/p>
@property (nonatomic) NSString *name;
等價于:
@property (nonatomic, assign) NSString *name;
assign 主要用于修飾數(shù)據(jù)類型,數(shù)據(jù)類型變量的內(nèi)存由編譯器自動管理。比如 NSInteger、CGFloat 等。
assign 修飾對象屬性時,其指向一個對象之后,不改變該對象的引用計數(shù)。即只引用已創(chuàng)建的對象,而不持有對象。
assign 修飾的屬性不持有對象,當其指向的對象在別處釋放后,該指針變?yōu)閼覓熘羔樢步幸爸羔槨?/p>
2. retain
retain 修飾的屬性會持有它所指向的對象,對象的引用計數(shù) +1,當不再需要使用該對象時需調(diào)用 release 釋放。
@property 修飾的變量會自動合成 getter 和 setter 方法,手動調(diào)用 setter 方法時需保留新值再釋放舊值。
3. copy
只能用于修飾對象屬性,將對象賦值給 copy 屬性時,一般情況下屬性會持有該對象的一份拷貝。
NSObject 雖然聲明了 copy 方法,但沒有實現(xiàn)。自定義的對象需實現(xiàn) NSCopying 協(xié)議的 copyWithZone:
方法才能實現(xiàn)拷貝。
copy 分為深拷貝和淺拷貝。對于 Foundation 中含有可變版本的對象類型,對其不可變版本的 copy 為淺拷貝,對于可變對象的 copy 為深拷貝。
- 不可變對象 NSArray:
@property (nonatomic, copy) NSArray *imArray;
測試示例:
NSArray *iArray = @[@"Jone"];
p.imArray = iArray;
查看 iArray
和 p.imArray
地址:
(lldb) p iArray
(__NSArrayI *) $0 = 0x0000000100204170 @"1 element"
(lldb) p p.imArray
(__NSArrayI *) $1 = 0x0000000100204170 @"1 element"
可以看出對于 NSArray 對象的拷貝為淺拷貝,它們?nèi)匀恢赶蛲粋€內(nèi)存地址。
- 可變對象 NSMutableArray
@property (nonatomic, copy) NSMutableArray *mArray;
測試示例:
NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.mArray = mArray;
查看 mArray
和 p.mArray
地址:
(lldb) p mArray
(__NSArrayM *) $0 = 0x0000000100300260 @"1 element"
(lldb) p p.mArray
(__NSArrayI *) $1 = 0x0000000100203a40 @"1 element"
可變對象 NSMutableArray 的拷貝為深拷貝,拷貝后指向不同的內(nèi)存地址。
雖然此時 p.mArray
和 mArray
代表不同的對象,但是此刻手癢打印出其元素的地址如下:
(lldb) p mArray[0]
(__NSCFConstantString *) $2 = 0x0000000100002108 @"Jone"
(lldb) p p.mArray[0]
(__NSCFConstantString *) $3 = 0x0000000100002108 @"Jone"
雖然對可變對象的 copy 為深拷貝,但其內(nèi)容相同的元素,仍然是對同一個對象的引用。
在使用含有可變版本的對象類型時,其不可變版本需用 copy 修飾符,比如: NSString、 NSArray 等,防止外部將可變版本的對象賦值給該對象屬性,然后該可變對象又被意外修改,會造成該屬性的內(nèi)容也被修改。
@property (nonatomic, retain) NSArray *imArray; // 不可變對象推薦使用 copy
NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.imArray = mArray;
[mArray addObject:@"iOSTalk"]; // 此時 p.imArray 被修飾,需使用 copy 修飾 imArray。
(lldb) po p.imArray
<__NSArrayM 0x1007005c0>(
Jone,
iOSTalk
)
在使用可變對象作為屬性時,慎用 copy 修飾符。如果將可變對象賦值給該屬性,該屬性持有的對象將會是不可變版本的,如果再對其執(zhí)行可變版本的方法,將會造成 crash。
@property (nonatomic, copy) NSMutableArray *mArray;// 可變對象慎用 copy
NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.mArray = mArray;
[p.mArray addObject:@"iOSTalk"]; // Crash
4. unsafe_unretained
語義等同于 assign,一般用于修飾對象,也可用于修飾數(shù)據(jù)類型。
ARC
當 ARC 有效時,以下屬性可用于屬性聲明:
屬性聲明的屬性 | 所有權(quán)修飾符 |
---|---|
assign | __unsafe_unretain 修飾符 |
copy | __strong 修飾符(賦值的是被復(fù)制的對象) |
retain | __strong 修飾符 |
strong | __strong 修飾符 |
unsafe_unretain | __unsafe_unretained 修飾符 |
weak | __weak 修飾符 |
1. assign/copy/retain
同 MRC 下的語義。
2. strong
strong 為 ARC 下屬性的默認內(nèi)存管理語義,語義等同于 retain。變量的所有權(quán)修飾符為 __strong
。
被賦值時會持有對象,阻止對象被釋放。
在 YYMemoryCache 中有這么一段代碼:
__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self _trimInBackground];
[self _trimRecursively];
});
這段代碼先是使用 _self
弱引用 self
再在 block 中使用 __strong
修飾符強引用 self
。使用 _self
是為了防止引用循環(huán)。而在 block 中使用強類型的 self
是為了防止 YYMemoryCache 實例被釋放后,已經(jīng)提交至隊列執(zhí)行的 block 中 _self
為 nil
。
3. weak
weak 修飾符弱引用對象,不改變對象的引用計數(shù),當其指向的對象被銷毀時,它會自動的置為 nil
。變量權(quán)限修飾符為 __weak
,常用于容易造成循環(huán)引用的地方。
__weak
實現(xiàn)原理:將 __weak
指向的對象地址作為鍵值,__weak
指針變量的地址注冊到 weak 表。
由于注冊、以及將 weak 變量置為 nil
有一定的開銷,如果大量的使用 weak 變量,會消耗一定的 CPU 資源。
4. unsafe_unretain
unsafe_unretain 語義等同于 assgin ,ARC 下編譯器不負責管理其修飾屬性的內(nèi)存。功能類似于 weak 但唯一不同的區(qū)別在于,weak 所指向的對象被銷毀后會被賦值為 nil
,而 unsafe_unretain 修飾的屬性變量成為懸掛指針。
由于 __weak
和 __unsafe_unretain
都不持有對象,如下兩行代碼都有引起編譯器警告。
id __weak obj = [[NSObject alloc] init];
id __unsafe_unretain obj = [[NSObject alloc] init];
__unsafe_unretain
可在結(jié)構(gòu)體中修飾對象, 而其他修飾符都不可以。
struct stringAndInt {
NSString * __unsafe_unretained s;
int x;
}