內(nèi)存管理語義

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;

查看 iArrayp.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;

查看 mArrayp.mArray 地址:

(lldb) p mArray
(__NSArrayM *) $0 = 0x0000000100300260 @"1 element"
(lldb) p p.mArray
(__NSArrayI *) $1 = 0x0000000100203a40 @"1 element"

可變對象 NSMutableArray 的拷貝為深拷貝,拷貝后指向不同的內(nèi)存地址。
雖然此時 p.mArraymArray 代表不同的對象,但是此刻手癢打印出其元素的地址如下:

(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 中 _selfnil

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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 開發(fā)中使用屬性很頻繁,常見的屬性內(nèi)存管理語義主要有 strong、copy、weak、assign,本次主要介紹下...
    Ching_Han閱讀 589評論 0 2
  • assign:'設(shè)置方法'只會針對「純量類型」如CGFloat、NSInteger等 strong:表明「擁有關(guān)系...
    Funcy1Day閱讀 403評論 0 0
  • 2016.5.15日上午,大雨,天氣冷,地點第二教學(xué)樓。大學(xué)里最特別的一課開始了,像是一場閱兵式,在這里我們被檢驗...
    相遇1827閱讀 343評論 0 0