@property相關
@property的本質是什么?
@property = ivar + getter + setter;
“屬性” (property)有兩大概念:ivar(實例變量)终吼、存取方法(access method = getter + setter)。
編譯器會自動寫出一套存取方法油宜,用以訪問給定類型中具有給定名稱的變量。 所以你也可以這么說:
@property = getter + setter;
ivar怜姿、getter慎冤、setter 是如何生成并添加到這個類中的?
“自動合成”( autosynthesis)
編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)沧卢。需要強調的是蚁堤,這個過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼但狭。除了生成方法代碼 getter披诗、setter 之外撬即,編譯器還要自動向類中添加適當類型的實例變量,并且在屬性名前面加下劃線呈队,以此作為實例變量的名字剥槐。在前例中,會生成兩個實例變量宪摧,其名稱分別為 _firstName與 _lastName粒竖。也可以在類的實現代碼里通過 @synthesize語法來指定實例變量的名字.
我為了搞清屬性是怎么實現的,曾經反編譯過相關的代碼,他大致生成了五個東西
OBJC_IVAR_屬性名稱 :該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode)几于,表示該變量距離存放對象的內存區(qū)域的起始地址有多遠蕊苗。
setter 與 getter 方法對應的實現函數
ivar_list :成員變量列表
method_list :方法列表
prop_list :屬性列表
也就是說我們每次在增加一個屬性,系統都會在 ivar_list 中添加一個成員變量的描述,在 method_list 中增加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性的描述,然后計算該屬性在對象中的偏移量,然后給出 setter 與 getter 方法對應的實現,在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確字節(jié)數,系統對象偏移量的指針類型進行了類型強轉.
什么情況使用 weak 關鍵字,相比 assign 有什么不同沿彭?
1朽砰、在 ARC 中,在有可能出現循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性
2、自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak喉刘;當然瞧柔,也可以使用strong。
不同點:
1饱搏、weak 此特質表明該屬性定義了一種“非擁有關系” (nonowning relationship)非剃。為這種屬性設置新值時置逻,設置方法既不保留新值推沸,也不釋放舊值。此特質同assign類似券坞, 然而在屬性所指的對象遭到摧毀時鬓催,屬性值也會清空(nil out)。 而 assign 的“設置方法”只會執(zhí)行針對“純量類型” (scalar type恨锚,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作宇驾。
assign放在棧上,weak放在堆上
2猴伶、assign 可以用非 OC 對象,而 weak 必須用于 OC 對象
怎么用 copy 關鍵字课舍?
用途:
- NSString、NSArray他挎、NSDictionary 等等經常使用copy關鍵字筝尾,是因為他們有對應的可變類型:NSMutableString、NSMutableArray办桨、NSMutableDictionary筹淫;
- block 也經常使用 copy 關鍵字,具體原因見官方文檔:Objects Use Properties to Keep Track of Blocks:
block 使用 copy 是從 MRC 遺留下來的“傳統”,在 MRC 中,方法內部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).
在 ARC 中寫不寫都行:
在 ARC 環(huán)境下呢撞,編譯器會根據情況自動將棧上的 block 復制到堆上损姜,比如以下情況:
block 作為函數返回值時
將 block 賦值給 __strong 指針時(property 的 copy 屬性對應的是這一條)
block 作為 Cocoa API 中方法名含有 using Block 的方法參數時
block 作為 GCD API 的方法參數時
其中饰剥, block 的 property 設置為 copy, 對應的是這一條:將 block 賦值給 __strong 指針時摧阅。
對于 block 使用 copy 還是 strong 效果是一樣的汰蓉,但寫上 copy 也無傷大雅,還能時刻提醒我們:編譯器自動對 block 進行了 copy 操作逸尖。如果不寫 copy 古沥,該類的調用者有可能會忘記或者根本不知道“編譯器會自動對 block 進行了 copy 操作”,他們有可能會在調用之前自行拷貝屬性值娇跟。這種操作多余而低效岩齿。你也許會感覺我這種做法有些怪異,不需要寫還依然寫苞俘。如果你這樣想盹沈,其實是你“日用而不知”,你平時開發(fā)中是經常在用我說的這種做法的吃谣,比如下面的屬性不寫copy也行乞封,但是你會選擇寫還是不寫呢?
下面做下解釋: copy 此特質所表達的所屬關系與 strong 類似岗憋。然而設置方法并不保留新值肃晚,而是將其“拷貝” (copy)。 當屬性類型為 NSString 時仔戈,經常用此特質來保護其封裝性关串,因為傳遞給設置方法的新值有可能指向一個 NSMutableString 類的實例。這個類是 NSString 的子類监徘,表示一種可修改其值的字符串晋修,此時若是不拷貝字符串,那么設置完屬性之后凰盔,字符串的值就可能會在對象不知情的情況下遭人更改墓卦。所以,這時就要拷貝一份“不可變” (immutable)的字符串户敬,確保對象中的字符串值不會無意間變動落剪。只要實現屬性所用的對象是“可變的” (mutable),就應該在設置新屬性值時拷貝一份尿庐。
這個寫法會出什么問題: @property (copy) NSMutableArray *array;
兩個問題:
1忠怖、添加,刪除,修改數組內的元素的時候,程序會因為找不到對應的方法而崩潰.因為 copy 就是復制一個不可變 NSArray 的對象;
2屁倔、使用了 atomic 屬性會嚴重影響性能 脑又;(該屬性使用了互斥鎖(atomic 的底層實現,老版本是自旋鎖,iOS10開始是互斥鎖--spinlock底層實現改變了问麸。)往衷,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序,這會帶來性能問題严卖,通過聲明 nonatomic 可以節(jié)省這些雖然很小但是不必要額外開銷席舍。)
如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter哮笆?
若想令自己所寫的對象具有拷貝功能来颤,則需實現 NSCopying 協議。如果自定義的對象分為可變版本與不可變版本稠肘,那么就要同時實現 NSCopying 與 NSMutableCopying 協議福铅。
具體步驟:
需聲明該類遵從 NSCopying 協議
實現 NSCopying 協議。該協議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;
@protocol 和 category 中如何使用 @property
1项阴、在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協議的對象能實現該屬性
2滑黔、category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現,需要借助于運行時的兩個函數:
3、objc_setAssociatedObject
4环揽、objc_getAssociatedObject
runtime 如何實現 weak 屬性
weak 此特質表明該屬性定義了一種“非擁有關系” (nonowning relationship)略荡。為這種屬性設置新值時,設置方法既不保留新值歉胶,也不釋放舊值汛兜。此特質同 assign 類似, 然而在屬性所指的對象遭到摧毀時通今,屬性值也會清空(nil out)粥谬。
那么 runtime 如何實現 weak 變量的自動置nil?
runtime 對注冊的類衡创, 會進行布局帝嗡,對于 weak 對象會放入一個 hash 表中晶通。 用 weak 指向的對象內存地址作為 key璃氢,當此對象的引用計數為0的時候會 dealloc,假如 weak 指向的對象內存地址是a狮辽,那么就會以a為鍵一也, 在這個 weak 表中搜索,找到所有以a為鍵的 weak 對象喉脖,從而設置為 nil椰苟。
而如果a是由 assign 修飾的,則: 在 b 非 nil 時树叽,a 和 b 指向同一個內存地址舆蝴,在 b 變 nil 時,a 還是指向該內存地址,變野指針洁仗。此時向 a 發(fā)送消息會產生崩潰层皱。
weak 修飾的指針默認值是 nil (在Objective-C中向nil發(fā)送消息是安全的)
@property中有哪些屬性關鍵字?/ @property 后面可以有哪些修飾符赠潦?
屬性可以擁有的特質分為四類:
1叫胖、原子性--- nonatomic 特質
在默認情況下,由編譯器合成的方法會通過鎖定機制確保其原子性(atomicity)她奥。如果屬性具備 nonatomic 特質瓮增,則不使用互斥鎖(atomic 的底層實現,老版本是自旋鎖哩俭,iOS10開始是互斥鎖--spinlock底層實現改變了绷跑。)。請注意凡资,盡管沒有名為“atomic”的特質(如果某屬性不具備 nonatomic 特質你踩,那它就是“原子的” ( atomic) ),但是仍然可以在屬性特質中寫明這一點讳苦,編譯器不會報錯带膜。若是自己定義存取方法,那么就應該遵從與屬性特質相符的原子性鸳谜。
2膝藕、讀/寫權限---readwrite(讀寫)、readonly (只讀)
3咐扭、內存管理語義---assign芭挽、strong、 weak蝗肪、unsafe_unretained袜爪、copy
4、方法名---getter=<name> 薛闪、setter=<name>
weak屬性需要在dealloc中置nil么辛馆?
在ARC環(huán)境無論是強指針還是弱指針都無需在 dealloc 設置為 nil , ARC 會自動幫我們處理
@synthesize和@dynamic分別有什么作用豁延?
1昙篙、@property有兩個對應的詞,一個是 @synthesize诱咏,一個是 @dynamic苔可。如果 @synthesize和 @dynamic都沒寫,那么默認的就是@syntheszie var = _var;
2袋狞、@synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法焚辅,那么編譯器會自動為你加上這兩個方法映屋。
3、@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現同蜻,不自動生成秧荆。(當然對于 readonly 的屬性只需提供 getter 即可)。假如一個屬性被聲明為 @dynamic var埃仪,然后你沒有提供 @setter方法和 @getter 方法乙濒,編譯的時候沒問題,但是當程序運行到 instance.var = someVar卵蛉,由于缺 setter 方法會導致程序崩潰颁股;或者當運行到 someVar = var 時,由于缺 getter 方法同樣會導致崩潰傻丝。編譯時沒問題甘有,運行時才執(zhí)行相應的方法,這就是所謂的動態(tài)綁定葡缰。
ARC下亏掀,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些泛释?
1 對應基本數據類型默認關鍵字是
atomic
readwrite
assign
2 對于普通的 Objective-C 對象
atomic
readwrite
strong
用@property聲明的NSString(或NSArray滤愕,NSDictionary)經常使用copy關鍵字,為什么怜校?如果改用strong關鍵字间影,可能造成什么問題?
1茄茁、因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
2魂贬、如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
copy 此特質所表達的所屬關系與 strong 類似。然而設置方法并不保留新值裙顽,而是將其“拷貝” (copy)付燥。 當屬性類型為 NSString 時,經常用此特質來保護其封裝性愈犹,因為傳遞給設置方法的新值有可能指向一個 NSMutableString 類的實例键科。這個類是 NSString 的子類,表示一種可修改其值的字符串甘萧,此時若是不拷貝字符串萝嘁,那么設置完屬性之后梆掸,字符串的值就可能會在對象不知情的情況下遭人更改扬卷。所以,這時就要拷貝一份“不可變” (immutable)的字符串酸钦,確保對象中的字符串值不會無意間變動怪得。只要實現屬性所用的對象是“可變的” (mutable),就應該在設置新屬性值時拷貝一份。
- 對非集合類對象的copy操作:(系統非集合類對象指的是 NSString, NSNumber ... 之類的對象)
在非集合類對象中:對 immutable 對象進行 copy 操作徒恋,是指針復制蚕断,mutableCopy 操作時內容復制;對 mutable 對象進行 copy 和 mutableCopy 都是內容復制入挣。用代碼簡單表示如下:
[immutableObject copy] // 淺復制
[immutableObject mutableCopy] //深復制
[mutableObject copy] //深復制
[mutableObject mutableCopy] //深復制
2亿乳、集合類對象的copy與mutableCopy
集合類對象是指 NSArray、NSDictionary径筏、NSSet ... 之類的對象葛假。下面先看集合類immutable對象使用 copy 和 mutableCopy 的一個例子:
[immutableObject copy] // 淺復制
[immutableObject mutableCopy] //單層深復制
[mutableObject copy] //單層深復制
[mutableObject mutableCopy] //單層深復制
- 淺復制(shallow copy):在淺復制操作時,對于被復制對象的每一層都是指針復制滋恬。
- 深復制(one-level-deep copy):在深復制操作時聊训,對于被復制對象,至少有一層是深復制恢氯。
- 完全復制(real-deep copy):在完全復制操作時带斑,對于被復制對象的每一層都是對象復制。
系統對象的copy與mutableCopy方法
不管是集合類對象勋拟,還是非集合類對象勋磕,接收到copy和mutableCopy消息時,都遵循以下準則:
- copy返回imutable對象敢靡;所以朋凉,如果對copy返回值使用mutable對象接口就會crash;
- mutableCopy返回mutable對象醋安;
集合對象的完全復制
方法一:使用 initWith***: copyItems:YES 方法
自定義集合對象使用這個方法杂彭,對象必須遵守NSCopying協議,并重寫- (id)copyWithZone:(NSZone *)zone方法吓揪。(系統類方法已經實現)亲怠。
方法二:先將集合進行歸檔,然后再解檔柠辞。
通常我們對模型數組完全復制团秽,先將模型數組轉換為data數組,再將data數組轉換為模型數組叭首,即可习勤。
最后說個題外的東西,在搜集資料的過程中焙格,發(fā)現一個有可能犯錯的點
NSString *str = @"string";
str = @"newString";
上面這段代碼图毕,在執(zhí)行第二行代碼后,內存地址發(fā)生了變化眷唉。乍一看予颤,有點意外囤官。按照 C 語言的經驗,初始化一個字符串之后蛤虐,字符串的首地址就被確定下來党饮,不管之后如何修改字符串內容,這個地址都不會改變驳庭。但此處第二行并不是對 str 指向的內存地址重新賦值刑顺,因為賦值操作符左邊的 str 是一個指針,也就是說此處修改的是內存地址饲常。
所以第二行應該這樣理解:將@"newStirng"當做一個新的對象捏检,將這段對象的內存地址賦值給str。
@synthesize合成實例變量的規(guī)則是什么不皆?假如property名為foo贯城,存在一個名為_foo的實例變量,那么還會自動合成新變量么霹娄?
實例變量 = 成員變量 = ivar
如果使用了屬性的話能犯,那么編譯器就會自動編寫訪問屬性所需的方法,此過程叫做“自動合成”( auto synthesis)犬耻。需要強調的是踩晶,這個過程由編譯器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法” (synthesized method)的源代碼枕磁。除了生成方法代碼之外渡蜻,編譯器還要自動向類中添加適當類型的實例變量,并且在屬性名前面加下劃線计济,以此作為實例變量的名字茸苇。
@interface CYLPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
在上例中,會生成兩個實例變量沦寂,其名稱分別為 _firstName 與 _lastName学密。也可以在類的實現代碼里通過 @synthesize 語法來指定實例變量的名字:
@implementation CYLPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
上述語法會將生成的實例變量命名為 _myFirstName 與 _myLastName ,而不再使用默認的名字传藏。一般情況下無須修改默認的實例變量名腻暮,但是如果你不喜歡以“下劃線”來命名實例變量,那么可以用這個辦法將其改為自己想要的名字毯侦。筆者還是推薦使用默認的命名方案哭靖,因為如果所有人都堅持這套方案,那么寫出來的代碼大家都能看得懂侈离。
總結下 @synthesize 合成實例變量的規(guī)則试幽,有以下幾點:
1 如果指定了成員變量的名稱,會生成一個指定的名稱的成員變量,
2 如果這個成員已經存在了就不再生成了
3 如果是 @synthesize foo; 還會生成一個名稱為foo的成員變量,也就是說:
如果沒有指定成員變量的名稱會自動生成一個屬性同名的成員變量,
4 如果是 @synthesize foo = _foo; 就不會生成成員變量了.
假如 property 名為 foo霍狰,存在一個名為 _foo 的實例變量抡草,那么還會自動合成新變量么饰及?
不會蔗坯。