前言:
@property關(guān)鍵字對(duì)每一個(gè)iOS開(kāi)發(fā)者而言都不是陌生的勇哗,或許很多同行會(huì)說(shuō)哥們現(xiàn)在都swift了你再談這個(gè)還有毛用呢亲桥,話說(shuō)的不錯(cuò)抄罕,swift已經(jīng)越來(lái)越普及(據(jù)說(shuō)Swift語(yǔ)言殺入TIOBE 3月編程語(yǔ)言排行榜Top 10)秃流,更多的開(kāi)發(fā)者和公司已經(jīng)嘗試用OC和Swift混編的模式進(jìn)行開(kāi)發(fā)祸挪,有放的開(kāi)的已經(jīng)Swift無(wú)限玩耍起來(lái)。但是歸根結(jié)底OC是每個(gè)iOS開(kāi)發(fā)者的起點(diǎn),無(wú)論是出于基本功的要求,還是針對(duì)面試要求我們都有必要去了解更多澜躺,當(dāng)然這里只是隨便聊點(diǎn)一些平時(shí)需要注意到的,而非全部描述抒蚜。
@property 的本質(zhì)
答案:成員變量(ivar) + setter方法 + getter方法
“屬性” (property)作為 OC 的一項(xiàng)特性掘鄙,主要的作用就在于封裝對(duì)象中的數(shù)據(jù)。 OC 對(duì)象通常會(huì)把其所需要的數(shù)據(jù)保存為各種實(shí)例變量嗡髓,然后通過(guò)“存取方法”(access method)來(lái)訪問(wèn)操漠。“設(shè)置方法” (setter)用于寫入變量值器贩,“獲取方法” (getter)用于讀取變量值颅夺,但是存取方法有著嚴(yán)格的命名規(guī)范朋截。
@property(nonatomic, strong) NSMutableString *name;
上面一行代碼是定義一個(gè)name屬性,系統(tǒng)會(huì)自動(dòng)幫我們生成成員變量(ivar) 吧黄、 setter方法 部服、getter方法,有一種說(shuō)法叫“自動(dòng)合成”拗慨。
開(kāi)發(fā)中有時(shí)我們需要重寫屬性的setter/getter方法廓八,這里提下簡(jiǎn)單注意:
- 如果同時(shí)重寫setter/getter方法,系統(tǒng)在不在自動(dòng)生成成員變量赵抢,需要手動(dòng)添加
- 重寫copy類型的setter方法時(shí)記得在賦值時(shí)用copy剧蹂,否則外面的copy就是擺設(shè)
@interface ViewController ()
@property(nonatomic, copy) NSString *age;
@end
@implementation ViewController
- (void)setAge:(NSString *)age {
// 賦值時(shí)用copy,否則外面的copy就是擺設(shè)
_age = age.copy;
}
@end
nonatomic 和 atomic(原子性和非原子性)
atomic(原子性):默認(rèn)屬性烦却,編輯器合成的getter和setter方法通過(guò)鎖定機(jī)制保證屬性原子性宠叼,多線程環(huán)境下防止資源搶奪。但是由于鎖定機(jī)制的保護(hù)其爵,開(kāi)銷比較大冒冬, 這會(huì)帶來(lái)性能問(wèn)題。
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
nonatomic(非原子性):不使用自旋鎖摩渺,效率提高简烤,一般移動(dòng)端開(kāi)發(fā)都會(huì)聲明為nonatomic,多線程情下為保護(hù)資源安全則會(huì)用深層的鎖定機(jī)制才行(GCD/NSOperation)摇幻。
總結(jié):在iOS開(kāi)發(fā)中横侦,你會(huì)發(fā)現(xiàn),幾乎所有屬性都聲明為 nonatomic绰姻。但是在開(kāi)發(fā) Mac OS 程序時(shí)建議使用 atomic 枉侧,因?yàn)閕OS使用nonatomic存在性能問(wèn)題而Mac OS則不存在(心臟大不擔(dān)心干活問(wèn)題v)。
copy龙宏、mutableCopy
說(shuō)到copy首先要談到“淺拷貝”和“深拷貝”:
淺拷貝:指針拷貝棵逊,不同指針向同一塊內(nèi)存伤疙,操作的是同一個(gè)對(duì)象
深拷貝:對(duì)象拷貝银酗,開(kāi)辟出一塊新的內(nèi)存空間來(lái)存儲(chǔ)拷貝的對(duì)象,和原對(duì)象再無(wú)關(guān)聯(lián)(除了剛拷貝出爐時(shí)長(zhǎng)的像而已)
- copy不可變對(duì)象(NSString, NSDictionary, NSArray等)徒像,淺拷貝黍特,得到不可變對(duì)象。
- copy可變對(duì)象(NSMutableString, NSMutableDictionary, NSMutableArray等)锯蛀,深拷貝灭衷,得到不可變對(duì)象。
- mutableCopy不可變對(duì)象(NSString, NSDictionary, NSArray等)旁涤,深拷貝翔曲,得到可變對(duì)象迫像。
- mutableCopy可變對(duì)象(NSMutableString, NSMutableDictionary, NSMutableArray等),深拷貝瞳遍,得到可變對(duì)象闻妓。
注意:copy出來(lái)得到的對(duì)象都是不可變的,而MutableCopy出來(lái)得到的對(duì)象都是可變的掠械。
問(wèn)題:字符串為什么用copy?
這里有個(gè)簡(jiǎn)單的小例子由缆,一個(gè)對(duì)象擁有一個(gè)可變字符串屬性,我們strong修飾而不用copy
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSMutableString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM = [NSMutableString stringWithString:@"jerry"];
self.name = strM;
NSLog(@"self.name = %@", self.name);
[strM appendString:@" say hello"];
NSLog(@"self.name = %@", self.name);
}
@end
控制臺(tái)輸入結(jié)果為:
2017-03-15 10:00:01.272 01[3109:32607] self.name = jerry
2017-03-15 10:00:01.272 01[3109:32607] self.name = jerry say hello
問(wèn)題分析:可變屬性不用copy猾蒂,外部變量修改時(shí)均唉,對(duì)象的屬性也會(huì)跟著修改,因?yàn)楸举|(zhì)上它們指向的是同一塊內(nèi)存肚菠,顯然這不是我們?cè)敢饪吹降奶蚣蚤_(kāi)發(fā)中對(duì)于NSString, NSDictionary, NSArray等這種有可變非可變之分的屬性都建議使用copy,目的是為了讓本對(duì)象的屬性不受外界影響蚊逢,使用 copy 無(wú)論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本限嫌。
block 也經(jīng)常使用 copy 關(guān)鍵字,目的是為了將對(duì)象從棧中保存到堆中时捌。這么做的原因:這個(gè)問(wèn)題得先談到block本身是干嘛的怒医,block是一個(gè)能工作的代碼單元,可以在任何需要的時(shí)候被調(diào)用奢讨,本質(zhì)上是輕量級(jí)的匿名函數(shù)稚叹,可以作為其他函數(shù)的參數(shù)或者返回值。通常我們使用到block就是做了做回調(diào)用的拿诸,如果block在棧中扒袖,定義block的代碼一旦過(guò)了作用域則會(huì)被釋放掉,而后需要觸發(fā)回調(diào)的時(shí)候就會(huì)找不block亩码,為了讓block持久保存季率,用copy修飾將其由棧中移到堆中。
weak 和 assign的區(qū)別
相同點(diǎn):
- 都會(huì)給被修飾的屬性指定一種“非擁有關(guān)系”描沟,為這種屬性賦值時(shí)即不保留新值也不釋放舊值飒泻,雖然持有對(duì)象,但是并不增加引用計(jì)數(shù)吏廉。
不同點(diǎn):
- weak屬性所指的對(duì)象遭到摧毀時(shí)泞遗,屬性值也會(huì)清空(nil out)。
- assign 的“設(shè)置方法”只會(huì)執(zhí)行針對(duì)“純量類型” (scalar type席覆,例如 CGFloat 或 NSlnteger 等)的簡(jiǎn)單賦值操作史辙。
- assign 可以用非 OC 對(duì)象,而 weak 必須用于 OC 對(duì)象
知識(shí)補(bǔ)充:弱引用是怎么實(shí)現(xiàn)的
系統(tǒng)對(duì)于每一個(gè)有弱引用的對(duì)象,都維護(hù)一個(gè)hash 表來(lái)記錄它所有的 weak 屬性, 用 weak 指向的對(duì)象內(nèi)存地址作為 key聊倔,當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為 0 時(shí)晦毙, 系統(tǒng)就會(huì)以對(duì)象地址為key在這個(gè) weak 表中搜索,找到對(duì)應(yīng) weak 屬性耙蔑,繼而置成 nil结序。
我們可以看出,弱引用的使用是有額外的開(kāi)銷的纵潦。雖然這個(gè)開(kāi)銷很小徐鹤,如果有些地方不是很需要弱引用的特性,就不應(yīng)該盲目使用弱引用邀层。
舉例:很多開(kāi)發(fā)者喜歡在手寫界面的時(shí)候返敬,將所有界面元素都設(shè)置成 weak 的,這與 Xcode 通過(guò) Storyboard或Xib拖拽生成的新變量是一致的寥院。但是這樣做并不合適:首先大部分 ViewController 的視圖對(duì)象的生命周期與 ViewController 本身是一致的劲赠,沒(méi)有必要額外做這個(gè)事情;其次弱引用可能會(huì)出現(xiàn)該對(duì)象剛被創(chuàng)建就被釋放秸谢;再者通過(guò)Storyboard或Xib拖拽生成的屬性默認(rèn)是weak修飾凛澎,這與蘋果之前的設(shè)計(jì)有關(guān),在 iOS6 之前應(yīng)用收到 Memory warning 時(shí)估蹄,系統(tǒng)會(huì)自動(dòng)調(diào)用當(dāng)前沒(méi)在界面上的 ViewController 的 viewDidUnload 方法塑煎,這些ViewController 的 View 會(huì)被 unLoad 掉,這個(gè)時(shí)候臭蚁,使用 weak 的視圖變量是有用的最铁,但是現(xiàn)在但是這個(gè)設(shè)計(jì)已經(jīng)被廢棄了。