知識基礎不夠牢。
一知半解诉瓦、想當然的用沒準將來會出大問題。
借用@我就叫Sunny怎么了的一句話:
《一個人iOS的基礎如何、只問一個property就夠了》
決定(深入?)總結(jié)一下暫時所能想到的property慧瘤。受篇幅所限、把一些相對冗余的原理探究放到了其他帖子固该。有興趣的童鞋可以去看(個人覺得還是挺有用的)
目錄
※ARC下锅减,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些伐坏?
※@property中有哪些屬性關鍵字怔匣?
原子性關鍵字
nonatomic/atomic
atomic---原子性(atomic內(nèi)部實現(xiàn))
※※※atomic聲明的屬性一定是線程安全的么
※※※copy關鍵字
※何時用copy關鍵字聲明
※※※block(block作用域/__block、__weak桦沉、__strong的原理)
NSString每瞒、NSArray、NSDictionary
※※深拷貝淺拷貝
※※weak/assign關鍵字
相似纯露、區(qū)別剿骨、assign如何修飾對象、ib對象為什么用weak
strong關鍵字
unsafe_unretained關鍵字
readonly/readwrite關鍵字
可選值: nullable埠褪、nonnull浓利、null_resettable、null_unspecified關鍵字
※ARC下组橄,不顯式指定任何屬性關鍵字時荞膘,默認的關鍵字都有哪些?
-
對應基本數(shù)據(jù)類型默認關鍵字是
atomic,readwrite,assign
-
對于普通的OC對象
atomic,readwrite,strong
※@property中有哪些屬性關鍵字玉工?
按照性質(zhì)可以分為四類羽资。(原子性/內(nèi)存管理語義/讀寫權(quán)限/setter/getter別名)
原子性: nonatomic、atomic
內(nèi)存管理:assign遵班、strong屠升、 weak潮改、unsafe_unretained、copy
讀寫權(quán)限:readwrite(讀寫)腹暖、readooly
方法名:getter=汇在、setter=
可選值: nullable、nonnull脏答、null_resettable糕殉、null_unspecified
其中atomic,nonatomic殖告,copy在setter/getter中實現(xiàn)阿蝶。
而weak和strong等則是直接作用于成員變量上。
-
原子性關鍵字
首先黄绩、nonatomic以及atomic除了自動合成中的setter/getter方法內(nèi)部不同羡洁、其他并沒有任何不同。
所以爽丹、如果你自己重寫了setter/getter筑煮。那么‘原子性’標簽將失效、只起到提示作用粤蝎。
nonatomic---非原子
- 單純的自動生成setter/getter方法真仲。
- 讀寫速度優(yōu)于atomic、如果不需要保證數(shù)據(jù)完整性(如多線程)诽里。盡量使用nonatomic袒餐。
atomic---原子性
在自動合成setter/getter方法的過程中、為對象添加條件鎖谤狡。
那么灸眼、atomic究竟是如何保護對象的原子性/這種保護是否需要可靠呢。
想再了解深入一些墓懂、可以參閱延展篇《原子性相關延伸》
-
※※※copy關鍵字
拋開一搜一大把的解釋焰宣、這里還需要分為兩部分來看:何時用copy關鍵字聲明/深拷貝淺拷貝
-
※何時用copy關鍵字聲明
為了確保屬性對象的完整性以及封裝性、只要實現(xiàn)屬性所用的對象是“可變的” 捕仔、就應該在設置新屬性值時拷貝一份匕积。
※※※block
- 在MRC中、block內(nèi)部的代碼塊依舊是在棧區(qū)的榜跌、使用copy可以把它放到堆區(qū)闪唆。
在ARC中、使用copy還是strong效果是一樣的钓葫。使用copy更多的是為了語義化(詳見官方文檔)悄蕾。
WechatIMG201.jpeg
那么、block究竟有哪些特殊。究竟__block帆调、__weak奠骄、__strong是如何工作的呢。
想再了解深入一些番刊、可以參閱延展篇《Block相關延伸》
※NSString含鳞、NSArray、NSDictionary
簡而言之芹务、因為父類指針可以指向子類對象蝉绷。通常我們?yōu)榱吮Wo變量的完整性、不希望這個對象被賦值之后又在不知情的情況下被改變锄禽。就像下面這樣:
@interface ViewController () @property (nonatomic,strong,readwrite) NSString * strongedStr; @property (nonatomic,copy,readwrite) NSString * copyedStr; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSMutableString * mOStr = [[NSMutableString alloc]initWithString:@"123123"]; self.strongedStr = mOStr; self.copyedStr = mOStr; NSLog(@"原對象改變前的strongedStr---%@",self.strongedStr); NSLog(@"原對象改變前的copyedStr---%@",self.copyedStr); [mOStr appendString:@"lalala"]; NSLog(@"原對象改變后的strongedStr---%@",self.strongedStr); NSLog(@"原對象改變后的copyedStr---%@",self.copyedStr); }
結(jié)果
原對象改變前的strongedStr---123123 原對象改變前的copyedStr---123123 原對象改變后的strongedStr---123123lalala 原對象改變后的copyedStr---123123
如上所示潜必、無論原對象如何改變。copy聲明的對象都可以保持當初被賦值的樣子沃但。
那么、copy到底是如何工作的佛吓、他和strong的聲明還有哪些不同宵晚、這里的copy和copy/mutableCopy是否相同呢。
想再了解深入一些维雇、可以參閱延展篇《strong&©聲明相關延伸》
-
※※深拷貝淺拷貝
關于深拷貝和淺拷貝的概念淤刃、這是基礎中的基礎。
淺拷貝
- 源對象和副本對象是同一對象吱型;
- 源對象(也就是副本對象)引用計數(shù)器+1逸贾。
本質(zhì):并未產(chǎn)生新對象。(由于源對象本身就不可變)津滞。
深拷貝
- 源對象和副本對象是不同的兩個對象
- 源對象引用計數(shù)器不變铝侵,副本對象計數(shù)器為1。
本質(zhì):產(chǎn)生了新對象触徐。(由于源對象本身就可變咪鲜、需要分離)。
再簡單點說
只有不可變對象的copy方式撞鹉,是淺復制疟丙,其他都是深復制。
copy與mutableCopy
不論源對象是否可變
- copy復制出的對象都是不可變對象
- mutableCopy復制出的對象都是可變對象
基本類型不允許copy
自定義對象
需要實現(xiàn)對應協(xié)議NSCopying/NSMutableCopying
- (id)copyWithZone:(NSZone *)zone{ PersonModel *model = [[[self class] allocWithZone:zone] init]; model.firstName = self.firstName; model.lastName = self.lastName; //未公開的成員--將其拷貝成不可變 model->_friends = [[NSMutableSet alloc] initWithSet:_friends copyItems:YES]; return model; } - (id)mutableCopyWithZone:(NSZone *)zone{ PersonModel *model = [[[self class] allocWithZone:zone] init]; model.firstName = self.firstName; model.lastName = self.lastName; //未公開的成員--將其拷貝成可變 model->_friends = [_friends mutableCopy]; return model; }
需要注意一點
對集合對象而言鸟雏、即使的深拷貝享郊。也只是拷貝了自身容器、對象內(nèi)部的元素依舊是淺拷貝孝鹊。
解決的方式就是手動對內(nèi)部元素進行copy炊琉。網(wǎng)上看到兩種比較方便的解決辦法。
-
※※weak/assign關鍵字
之所以把這兩個關鍵字放在一起講惶室。是因為在ARC下温自、他倆很相似玄货。
相似點:
- 都不會讓對象的引用計數(shù)+1;
也有區(qū)別:
weak在指針對象釋放的時候會自動置nil、assign則不進行任何操作悼泌。
weak修飾對象類型松捉、assign修飾基本類型。
其實這兩個區(qū)別本質(zhì)上就是第一種馆里、因為基本類型通常被分配在棧上隘世、由系統(tǒng)自動釋放。而如果對象釋放鸠踪、指針指向沒有釋放丙者。那么就會出現(xiàn)野指針。
WechatIMG214.jpeg
什么時候用weak:
當你不希望你的屬性营密、需要被本類手動釋放的時候械媒。
最典型的例子就是代理、當外界主體被消滅评汰。本類delegate指針也指向nil纷捞。
什么時候用assign:
幾乎所有的普通類型變量。
assign能不能修飾對象:
-
可以被去。
既然我們知道二者的區(qū)別主儡。那么只要讓assign聲明的對象、在本身釋放的時候?qū)⒅羔樦赶蛑胣il就可以了惨缆。
具體寫要分情況糜值。
比如可以在自己VC被釋放的時候、將引用自己的代理手動置nil坯墨。-(void)dealloc { self.xxx.delegate = nil; }
-
如何讓沒有聲明weak屬性的對象寂汇、擁有weak屬性。
weak聲明的對象在runtime布局的時候畅蹂、會被放入一個hash表中健无。
以對象的內(nèi)存地址作為key、本身作為value液斜。
當對象被釋放累贤、再以其地址作為索引去搜索hash表。
找出value少漆、并將其置nil臼膏。
具體實現(xiàn)有空再新開帖補上~
-
ib創(chuàng)建的對象為什么用weak
簡單來講、ib創(chuàng)建的對象在ib中就已經(jīng)被其父view強引用了一次示损。
只要父view沒有被釋放渗磅、其自然不會被釋放。
然后、怎么證明呢始鱼?#define TLog(prefix,Obj) {NSLog(@"變量指針:%p,變量值地址:%p, 指向?qū)ο笾担?@, 變量類型:%@--%@",&Obj,Obj,Obj,[Obj class],prefix);} @interface ViewController () @property(nonatomic,weak) UIButton *btn; @property(nonatomic,weak) UIButton *btn2; @end @implementation ViewController - (void)viewDidLoad { [self test]; TLog(@"btn", _btn); TLog(@"btn2", _btn2); } -(void)test { UIButton *button = [[UIButton alloc]init]; _btn = button; [self.view addSubview:_btn]; UIButton *button2 = [[UIButton alloc]init]; _btn2 = button2; }
我沒有將button2添加到view上仔掸、在出了局部作用域之后、button2被系統(tǒng)釋放医清。_btn2自然也指向空對象起暮。
打印結(jié)果
變量指針:0x7f8538502600,變量值地址:0x7f8538609af0, 指向?qū)ο笾担?lt;UIButton: 0x7f8538609af0; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x6000000286c0>>, 變量類型:UIButton--btn 變量指針:0x7f8538502608,變量值地址:0x0, 指向?qū)ο笾担?null), 變量類型:(null)--btn2
-
strong關鍵字
其實strong想不起好說的、因為都用的太熟了~
只有當strong不能滿足我們需求的情況下会烙、才會去用其他的修飾符负懦。
- 當需要解決循環(huán)引用或者自身已經(jīng)對其有過一次強引用的時候、用weak柏腻。
- 當NSString纸厉、NSArray、NSDictionary等需要保持自身完整性五嫂、不希望外界修改或者傳進來一個可變類型的時候颗品、用copy。
-
unsafe_unretained關鍵字
我入行的晚沃缘、unsafe_unretained基本沒用過~
只知道是對象版本的assign抛猫。
修飾對象、但不會自動置nil孩灯。
但因為不需要創(chuàng)建hash表檢測對象的存活情況、在明確知曉生命周期的時候逾滥。unsafe_unretained相比weak有一定的性能提升峰档。
-
readonly/readwrite關鍵字
表示屬性的讀寫權(quán)限。
- readwrite系統(tǒng)會自動合成setter&&getter方法
- readonly系統(tǒng)則只合成了get方法寨昙。
似乎也沒什么太深的東西讥巡、不過還是有幾點可以提一句的。
- 如果你自己實現(xiàn)了set/get或者聲明了@dynamic(這個不屬于property舔哪、所以先不寫~大概就是告訴系統(tǒng)不要幫你合成)欢顷。那么系統(tǒng)將不再為你自動合成。
-
readonly雖然不可以直接修改捉蚤、但是可以用KVC修改抬驴。
- 網(wǎng)上有說KVO無法監(jiān)聽readonly的屬性、但只要你實現(xiàn)了內(nèi)部的set方法缆巧。當對象被釋放的時候布持、你依然可以收到通知。(具體什么時候用呢陕悬、比如單例對象的代理题暖。當外部代理對象釋放、你可以這樣監(jiān)聽。讓自身調(diào)用某個方法)胧卤。
-
getter=/setter=
-
setter= 重寫set聲明唯绍。沒見誰用...如果哪位大佬有實用的地方、可以告訴小弟枝誊、不勝感激况芒。
-
setter= 重寫get聲明。起到方便閱讀的作用唄~
@property (nonatomic, getter=isOn) BOOL on;
-
可選值聲明
nullable侧啼、nonnull牛柒、null_resettable、null_unspecified
這四個聲明是xcode6新出的痊乾、應該是為了迎合swift中的optional皮壁、non-optional吧。方便我們的混編哪审、而且可以讓代碼更加規(guī)范蛾魄、易懂。在發(fā)生了不符合規(guī)定的行為時湿滓、編譯器會發(fā)出警告滴须。(雖然不會崩潰)
-
nullable:可以為空
作為屬性、可以有三種聲明規(guī)范:
// 方式一:
@property (nonatomic, copy, nullable) NSString *name;
// 方式二:
@property (nonatomic, copy) NSString *_Nullable name;
// 方式三:
@property (nonatomic, copy) NSString *__nullable name;
也可以聲明在方法里
- (nullable NSString *)test1 {
return nil;
}
- (NSString * _Nullable)test2 {
return nil;
}
- (NSString * __nullable)test3 {
return nil;
}
規(guī)律就這樣~沒啥必要再總結(jié)下劃線和大小寫了吧叽奥。
效果長這樣:
-
nonnull:不能為空
和nullable相對扔水、用法相同。
但有一點需要注意朝氓。
oc中的nonnull和swift中的non-optional不同魔市。即使聲明成nonnull、你依然可以讓他為空赵哲。編譯器只會提示警告待德、依舊會通過編譯。
-
null_unspecified不確定是否為空枫夺。
和nonnull/nullable一樣将宪、有三種聲明方法。
-
null_resettable:get:不能返回空, set可以為空
只有一種聲明方法
@property (nonatomic, copy, null_resettable) NSString *name;
需要注意的是:如果選用此聲明橡庞、編譯器會提示你自己實現(xiàn)get/set方法去處理nil的情況较坛。
我一般是在懶加載的時候用。你可以把我置nil毙死、但只要你需要我燎潮、我就是在的。(控制器的view也是)
-
NS_ASSUME_NONNULL_BEGIN以及NS_ASSUME_NONNULL_END
在此之間的屬性扼倘、都會被設置為nonnull(非空)
@interface ViewController ()
NS_ASSUME_NONNULL_BEGIN
@property (nonatomic,weak) MyObject * obj;
@property (nonatomic,assign) MyObject * obj2;
@property(nonatomic,null_unspecified) UIButton *btn;
@property(nonatomic,weak) UIButton *btn2;
@property (nonatomic, copy,null_resettable) NSString * name;
NS_ASSUME_NONNULL_END
@end
-
結(jié)尾
暫時就能想到這么多關于property的東西确封。如果有問題除呵、歡迎留言。哪里寫的不對更歡迎斧正爪喘。
感謝您的閱讀颜曾、希望我的這篇總結(jié)能起到一些幫助。
最后
本文主要是自己的學習與總結(jié)秉剑。如果文內(nèi)存在紕漏泛豪、萬望留言斧正。如果不吝賜教小弟更加感謝侦鹏。