ios屬性修飾符總結(jié)

前言

很多剛接觸iOS的朋友,對property的可選參數(shù)如何使用,什么情況下使用哪種選項不了解,也問了我很多這方面的知識,雖然知道怎么用,但是有些說不出其區(qū)別。在這里,再次深入學(xué)習(xí)一遍,對copy/strong/weak/__weak/__strong/assign的使用場景總結(jié)總結(jié)。如果有說得不對的地方,請指出。如果有疑問,請私聊我,或者直接回復(fù)我。

自動引用計數(shù)

原文檔關(guān)于自動引用說明:

Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations, ARC allows you to concentrate on the interesting code, the object graphs, and the relationships between objects in your application.

翻譯過來就是:Automatic Reference Counting (ARC)是一個編譯器的特性,提供了對iOS對象的自動內(nèi)存管理,ARC在編譯期間自動在適當(dāng)?shù)牡胤教砑?code>ObjC對象的retainrelease操作代碼,而不需要我們關(guān)心。

ARC在編譯期間,根據(jù)Objective-C對象的存活周期,在適當(dāng)?shù)奈恢锰砑?code>retain和release代碼。從概念上講,ARC與手動引用計數(shù)內(nèi)存管理遵循同樣的內(nèi)存管理規(guī)則,但是ARC也無法防止循環(huán)強(qiáng)引用。

ARC還引入了新的修飾符來修飾變量和聲明屬性:

  • 聲明變量的修飾符:__strong, __weak, __unsafe_unretained, __autoreleasing;
  • 聲明屬性的修飾符:strong, weak, unsafe_unretained。
  • 對象和Core Foundation-style對象直接的轉(zhuǎn)換修飾符號:__bridge,__bridge_retained或CFBridgingRetain, __bridge_transfer或CFBridgingRelease。
  • 對于線程的安全,有nonatomic,這樣效率就更高了,但是不是線程的。如果要線程安全,可以使用atomic,這樣在訪問是就會有線程鎖。
  • 記住內(nèi)存管理法則:誰使對象的引用計數(shù)+1,不再引用時,誰就負(fù)責(zé)將該對象的引用計數(shù)-1。

實戰(zhàn)性學(xué)習(xí)

下面我們來聲明一個Person類來學(xué)習(xí):

@interface Person : NSObject

// 注意:蘋果有命名規(guī)范的,命名屬性時,不能以copy開頭。
// 如果下面的屬性聲明為copyString,會編譯不通過。
@property (nonatomic, copy) NSString *copiedString;

// 默認(rèn)會是什么呢?
@property (nonatomic) NSString *name;
// 默認(rèn)是strong類型
@property (nonatomic) NSArray *array;

@end

如果屬性沒有指定類型,默認(rèn)是什么呢?其實是strong。如果證明呢?驗證方法:分別將array屬性的類型分別設(shè)置為weak, assign,strong,不設(shè)置,這四種情況的結(jié)果分別是:第一種打印為空,第二種直接直接崩潰,第三種和最后一種是可以正常使用。如下面的驗證代碼:

Person *lili = [[Person alloc] init];
lili.name = @"LiLi";
lili.copiedString = @"LiLi\' father is LLL";
lili.array = @[@"謝謝", @"感謝"];
  
NSArray *otherArray = lili.array;
lili = nil;
NSLog(@"%@", otherArray);

再繼續(xù)添加下面的代碼。默認(rèn)聲明變量的類型為__strong類型,因此上面的NSArray *otherArray = lili.array;__strong NSArray *otherArray = lili.array;是一樣的。如果我們要使用弱引用,特別是在解決循環(huán)強(qiáng)引用時就特別重要了。我們可以使用__weak聲明變量為弱引用,這樣就不會增加引用計數(shù)值。

__strong NSArray *strongArray = otherArray;
otherArray = nil;
// 打印出來正常的結(jié)果。
NSLog(@"strongArray = %@", strongArray);
  
__weak NSArray * weakArray = strongArray;
strongArray = nil;
// 打印出來:null
NSLog(@"weakArray: %@", weakArray);

xib/storybard連接的對象為什么可以使用weak

@property (nonatomic, weak) IBOutlet UIButton *button;

像上面這行代碼一樣,在連接時自動生成為weak。因為這個button已經(jīng)放到view上了,因此只要這個View不被釋放,這個button的引用計數(shù)都不會為0,因此這里可以使用weak引用。

如果我們不使用xib/storyboard,而是使用純代碼創(chuàng)建呢?

@property (nonatomic, weak) UIButton *button;

補(bǔ)充:有朋友反映這里說得不夠詳細(xì),感謝這位朋友的反饋。

使用weak時,由于button在創(chuàng)建時,沒有任何強(qiáng)引用,因此就有可能提前釋放。Xcode編譯器會告訴我們,這里不能使用weak。因此我們需要記住,只要我們在創(chuàng)建以后需要使用它,我們必須保證至少有一個強(qiáng)引用,否則引用計數(shù)為0,就會被釋放掉。對于上面的代碼,就是由于在創(chuàng)建時使用了weak引用,因此button的引用計數(shù)仍然為0,也就是會被釋放,編譯器在編譯時會檢測出來的。

這樣寫,在創(chuàng)建時通過self.button = ...就是出現(xiàn)錯誤,因為這是弱引用。所以我們需要聲明為強(qiáng)引用,也就是這樣:

@property (nonatomic, strong) UIButton *button;

block聲明使用copy

在使用block時,盡量使用typedef來起一個別名,這樣更容易閱讀。使block作為屬性時,盡量使用copy。蘋果官方有說明,因為有上下文變量捕獲,應(yīng)該使用copy。但是很多朋友說使用strong也一樣,沒有區(qū)別。目前,沒有辦法說明這使用strong與copy導(dǎo)致有沒有區(qū)別。不過,筆者一直都會使用copy來聲明Block,而不是strong:

typedef void (^TestBlock)(NSString *name);

@property (nonatomic, copy) TestBlock testBlock;

字符串使用copy

對于字符串,通常都是使用copy的方式。雖然使用strong也沒有沒有問題,但是事實上在開發(fā)中都會使用copy。為什么這么做?因為對于字符串,我們希望是一次內(nèi)容的拷貝,外部修改也不會影響我們的原來的值,而且NSString類遵守了NSCopying, NSMutableCopying, NSSecureCoding協(xié)議。

下面時使用copy的方式,驗證如下:

NSString *hahaString = @"哈哈";
NSString *heheString = [hahaString copy];
// 哈哈, 哈哈
NSLog(@"%@, %@", hahaString, heheString);
heheString = @"呵呵";
// 哈哈, 呵呵
NSLog(@"%@, %@", hahaString, heheString);

我們修改了heheString,并不會影響到原來的hahaString。copy一個對象變成新的對象(新內(nèi)存地址) 引用計數(shù)為1 原來對象計數(shù)不變。

屬性聲明修飾符

屬性聲明修飾符有:strong, weak, unsafe_unretained, readWrite,默認(rèn)strong, readWrite的。

  • strong:strong和retain相似,只要有一個strong指針指向?qū)ο?,該對象就不會被銷毀
  • weak:聲明為weak的指針,weak指針指向的對象一旦被釋放,weak的指針都將被賦值為nil;
  • unsafe_unretained:用unsafe_unretained聲明的指針,指針指向的對象一旦被釋放,這些指針將成為野指針。
@property (nonatomic, copy) NSString *name;
// 一旦所指向的對象被釋放,就會成為野指針
@property (nonatomic, unsafe_unretained) NSString *unsafeName;

lili.name = @"Lili";
lili.unsafeName = lili.name;
lili.name = nil;
// unsafeName就變成了野指針。這里不會崩潰,因為為nil.
NSLog(@"%@", lili.unsafeName);

深拷貝與淺拷貝

關(guān)于淺拷貝,簡單來說,就像是人與人的影子一樣。而深拷貝就像是夢幻西游中的龍宮有很多個長得一樣的龍宮,但是他們都是不同的精靈,因此他們各自都是獨立的。

我相信還有不少朋友有這樣一種誤解:淺拷貝就是用copy,深拷貝就是用mutableCopy。如果有這樣的誤解,一定要更正過來。copy只是不可變拷貝,而mutableCopy是可變拷貝。比如,

// 那么arr是不可變的
NSArray *arr = [modelsArray copy];

// 那么ma是可變的。
NSMutableArray *ma = [modelsArray mutableCopy];

舉個例子:

lili.array = [@[@"謝謝", @"感謝"] mutableCopy];
NSMutableArray *otherArray = [lili.array copy];
lili.array[0] = @"修改了謝謝";

// 打?。?謝謝 修改了謝謝
// 說明數(shù)組里面是字符串時,直接使用copy是相當(dāng)于深拷貝的。
NSLog(@"%@ %@", otherArray[0], lili.array[0]);

這里就是淺拷貝,但是由于數(shù)組中的元素都是字符串,因此不會影響原來的值。

數(shù)組中是對象時:

NSMutableArray *personArray = [[NSMutableArray alloc] init];
Person *person1 = [[Person alloc] init];
person1.name = @"lili";
[personArray addObject:person1];
  
Person *person2 = [[Person alloc] init];
person2.name = @"lisa";
[personArray addObject:person2];
  
// 淺拷貝
NSArray *newArray = [personArray copy];
Person *p = newArray[0];
p.name = @"lili的名字被修改了";
  
// 打印結(jié)果:lili的名字被修改了
// 說明這邊修改了,原來的數(shù)組對象的值也被修改了。雖然newArray和personArray不是同一個數(shù)組,不是同一塊內(nèi)存,
// 但是實際上兩個數(shù)組的元素都是指向同一塊內(nèi)存。
NSLog(@"%@", ((Person *)(personArray[0])).name);
深拷貝,其實就是對數(shù)組中的所有對象都創(chuàng)建一個新的對象:

NSMutableArray *personArray = [[NSMutableArray alloc] init];
Person *person1 = [[Person alloc] init];
person1.name = @"lili";
[personArray addObject:person1];
  
Person *person2 = [[Person alloc] init];
person2.name = @"lisa";
[personArray addObject:person2];
  
// 深拷貝
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (Person *p in personArray) {
Person *newPerson = [[Person alloc] init];
newPerson.name = p.name;

[newArray addObject:newPerson];
}
Person *p = newArray[0];
p.name = @"lili的名字被修改了";
  
// 打印結(jié)果:lili
NSLog(@"%@", ((Person *)(personArray[0])).name);

閱讀更多關(guān)于深拷貝與淺拷貝

Getter/Setter

在ARC下,getter/setter的寫法與MRC的不同了。我面試過一些朋友,筆試這關(guān)就寫得很糟(不包括算法)。通常在筆試時都會讓重寫一個屬性的Getter/Setter方法。

@property (nonatomic, strong) NSMutableArray *array;

- (void)setArray:(NSMutableArray *)array {
  if (_array != array) {
    _array = nil;
    
    _array = array;
  }
}

如果是要重寫getter就去呢?就得增加一個變量了,如果同時重寫getter/setter方法,就不會自動生成_array變量,因此我們可以聲明一個變量為_array:

- (void)setArray:(NSMutableArray *)array {
  if (_array != array) {
    _array = nil;
    
    _array = array;
  }
}

- (NSMutableArray *)array {
  return _array;
}

總結(jié)

關(guān)于屬性的這些選項的學(xué)習(xí),做一下總結(jié):

  • 所有的屬性,都盡可能使用nonatomic,以提高效率,除非真的有必要考慮線程安全。
  • NSString:通常都使用copy,以得到新的內(nèi)存分配,而不只是原來的引用。
  • strong:對于繼承于NSObject類型的對象,若要聲明為強(qiáng)使用,使用strong,若要使用弱引用,使用__weak來引用,用于解決循環(huán)強(qiáng)引用的問題。
  • weak:對于xib上的控件引用,可以使用weak,也可以使用strong。
  • __weak:對于變量的聲明,如果要使用弱引用,可以使用weak,如:weak typeof(Model) weakModel = model;就可以直接使用weakModel了。
  • __strong:對于變量的聲明,如果要使用強(qiáng)引用,可以使用strong,默認(rèn)就是strong,因此不寫與寫__strong聲明都是一樣的。
  • __unsafe_unretained:在所引用的對象被釋放后,該指針就成了野指針,不好控制。對于C語言的指針,我們會使用這個來聲明。
  • __autoreleasing:如果要在循環(huán)過程中就釋放,可以手動使用__autoreleasing來聲明將之放到自動釋放池。

參考資料

官方文檔關(guān)于自動引用計數(shù)內(nèi)存管理介紹:官方文檔說明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.1 什么是自動引用計數(shù) 概念:在 LLVM 編譯器中設(shè)置 ARC(Automaitc Reference Co...
    __silhouette閱讀 5,469評論 1 17
  • 307、setValue:forKey和setObject:forKey的區(qū)別是什么? 答:1, setObjec...
    AlanGe閱讀 1,717評論 0 1
  • 1、一味地死磕自己,尋求一個好的結(jié)果,倒不如在做事的過程中,享受事情本身的樂趣。
    石話石說簡書閱讀 214評論 0 0
  • 李白 我十步殺一人 卻敗給你的一個眼神 我有酒有你 此生足矣 孫悟空 我一生放蕩不羈 就是神也阻止不了 掀起一陣腥...
    笑看可樂閱讀 144評論 0 0

友情鏈接更多精彩內(nèi)容