iOS開發(fā)—屬性關(guān)鍵字詳解
@Property
什么是屬性榜轿?
- 屬性
(property)
是Objective-C
的一項特性短条,用于封裝對象中的數(shù)據(jù)剑辫。這一特性可以令編譯器自動編寫與屬性相關(guān)的存取方法阱飘,并且保存為各種實例變量摘昌。 - 屬性的本質(zhì)是實例變量與存取方法的結(jié)合速妖。
@property = ivar + getter + setter
- 實現(xiàn)流程:
- 每次增加一個屬性,系統(tǒng)都會在
ivar_list
中添加一個成員變量的描述,在method_list
中增加setter
與getter
方法的描述聪黎,在prop_list
中增加一個屬性的描述罕容,計算該屬性在對象中的偏移量,然后給出setter
與getter
方法對應的實現(xiàn)。在setter
方法中從偏移量的位置開始賦值,在getter
方法中從偏移量開始取值,為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對象偏移量的指針類型進行了類型強轉(zhuǎn)锦秒。
Property的默認設置
- 基本數(shù)據(jù)類型:
atomic
,readwrite
,assign
- 對象類型:
atomic
,readwrite
,strong
??:注意:考慮到代碼可讀性以及日常代碼修改頻率露泊,規(guī)范的編碼風格中關(guān)鍵詞的順序是:原子性、讀寫權(quán)限旅择、內(nèi)存管理語義惭笑、getter/getter。
關(guān)鍵字
關(guān)鍵字 | 解釋 |
---|---|
atomic | 原子性訪問 |
nonatomic | 非原子性訪問生真,多線程并發(fā)訪問會提高性能 |
readwrite | 此標記說明屬性會被當成讀寫的沉噩,這也是默認屬性 |
readonly | 此標記說明屬性只可以讀,也就是不能設置汇歹,可以獲取 |
strong | 打開ARC時才會使用屁擅,相當于retain |
weak | 打開ARC時才會使用,相當于assign产弹,可以把對應的指針變量置為nil |
assign | 不會使引用計數(shù)加1派歌,也就是直接賦值 |
unsafe_unretain | 與weak類似,但是銷毀時不自動清空痰哨,容易形成野指針 |
copy | 與strong類似胶果,設置方法會拷貝一份副本。一般用于修飾字符串和集合類的不可變版斤斧, block用copy修飾 |
詳解copy
-
copy
語法的作用:- 產(chǎn)生副本
-
copy
返回的是不可變的副本 -
mutableCopy
返回的是可變的副本
-
- 修改了副本并不會影響源對象早抠,修改了源對象,并不會影響副本撬讽。
- 產(chǎn)生副本
-
copy
使用場景:-
NSString
蕊连、NSArray
、NSictionary
等等經(jīng)常使用copy
關(guān)鍵字,是因為他們有對應的可變類型:NSMutableString
游昼、NSMutableArray
甘苍、NSMutableDictionary
.為確保對象中的屬性值不會無意間變動,應該在設置新屬性值時拷貝一份,保護其封裝性 -
block
,也經(jīng)常使用copy
使用
copy
是從MRC
遺留下來的“傳統(tǒng)”,在MRC
中,方法內(nèi)部的block
是在棧區(qū)的,使用copy
可以把它放到堆區(qū).在
ARC
中寫不寫都行:對于block
使用copy
還是strong
效果是一樣的,但是建議寫上copy
,因為這樣顯示告知調(diào)用者“編譯器會自動對 block 進行了copy
操作.
-
-
為什么用
@property
聲明的NSString
(或NSArray,NSDictionary
)經(jīng)常使用copy
關(guān)鍵字,為什么?如果改用strong
關(guān)鍵字,可能造成什么問題?- 因為父類指針可以指向子類對象,使用
copy
的目的是為了讓本對象的屬性不受外界影響,使用copy
無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本. 如果我們使用是strong
,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
- 因為父類指針可以指向子類對象,使用
-
如何讓自定義類可以用
copy
修飾符?如何重寫帶copy
關(guān)鍵字的setter
?- 若想令自己所寫的對象具有拷貝功能,則需實現(xiàn)
NSCopying
協(xié)議烘豌。如果自定義的對象分為可變版本與不可變版本,那么就要同時實現(xiàn)NSCopyiog
與NSMutableCopying
協(xié)議,不過一般沒什么必要,實現(xiàn)NSCopying
協(xié)議就夠了
- (id)copyWithZone:(NSZone *)zone { NSObject *copyObj = [[NSObject allocWithZone:zone] init]; copyObj.name = self.name; return copyObj; } - (void)setName;(Mitchell*)name { _name = [name copy]; }
- 若想令自己所寫的對象具有拷貝功能,則需實現(xiàn)
atomic與nonatomic
- 什么是原子性载庭?
- 并發(fā)編程中確保其操作具備整體性,系統(tǒng)其它部分無法觀察到中間步驟廊佩,只能看到操作前后的結(jié)果
- atomic:原子性的囚聚,編譯器會通過鎖定機制確保
setter
和getter
的完整性。 - nonatomic:非原子性的标锄,不保證
setter
和getter
的完整性顽铸。 - 區(qū)別:由于要保證操作完整,
atomic
速度比較慢料皇,線程相對安全跋破;nonatomic
速度比較快簸淀,但是線程不安全瓶蝴。atomic
也不是絕對的線程安全毒返,當多個線程同時調(diào)用setter
和getter
時,就會導致獲取的值不一樣舷手。由于鎖定機制開銷較大拧簸,一般iOS開發(fā)中會使用nonatomic
,而macOS
中使用atomic
通常不會有性能瓶頸男窟。 - 如果對這塊不太了解盆赤,你可以看一下這篇文章atomic到底不安全在哪?
readwrite與readonly
- 讀寫權(quán)限不寫時默認為 readwrite 歉眷。一般可在 .h 里寫成readonly牺六,只對外提供讀取,在 .m 的Extension中再設置為 readwrite 可進行寫入汗捡。
//.h文件
@interface MyClass : NSObject
@property (nonatomic, readonly, copy) NSString *name;
@end
//.m文件
@interface MyClass()
@property (nonatomic, readwrite, copy) NSString *name;
@end
比較strong與copy
- 相同之處:是用于修飾表示擁有關(guān)系的對象淑际。
- 不同之處:
strong
復制是多個指針指向同一個地址,而copy
的復制是每次會在內(nèi)存中復制一份對象扇住,指針指向不同的地址春缕。-
NSString
、NSArray
艘蹋、NSDictionary
等不可變對象用copy修飾锄贼,因為有可能傳入一個可變的版本,此時能保證屬性值不會受外界影響女阀。
-
- 注意??:若用
strong
修飾NSArray
宅荤,當數(shù)組接收一個可變數(shù)組,可變數(shù)組若發(fā)生變化浸策,被修飾的屬性數(shù)組也會發(fā)生變化冯键,也就是說屬性值容易被篡改;若用copy
修飾NSMutableArray
的榛,當試圖修改屬性數(shù)組里的值時琼了,程序會崩潰,因為數(shù)組被復制成了一個不可變的版本夫晌。
比較assign雕薪、weak、unsafe_unretain
- 相同之處:都不是強引用
- 不同之處:
weak
引用的 OC 對象被銷毀時, 指針會被自動清空晓淀,不再指向銷毀的對象所袁,不會產(chǎn)生野指針錯誤;unsafe_unretain
引用的 OC 對象被銷毀時, 指針并不會被自動清空, 依然指向銷毀的對象凶掰,很容易產(chǎn)生野指針錯誤:EXC_BAD_ACCESS燥爷;assign
修飾基本數(shù)據(jù)類型蜈亩,內(nèi)存在棧上由系統(tǒng)自動回收。
@synthesize 和 @dynamic
@property
有兩個對應的詞,一個是@synthesize
,一個是@dynamic
前翎。
如果@synthesize
和@dynamic
都沒寫,那么默認的就是
@syntheszie var = _var;
@synthesize
的語義是如果你沒有手動實現(xiàn) setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法稚配。@dynamic
告訴編譯器:屬性的setter
與getter
方法由用戶自己實現(xiàn),不自動生成。(當然對于readonly
的屬性只需提供getter
即可)
假如一個屬性被聲明為@dynamic var
港华;然后你沒有提供@setter
方法和@getter
方法,編譯的時候沒問題,但是當程序運行到instance.var = someVar
,由于缺setter
方法會導致程序崩潰;
或者當運行到someVar = instance.var
時,由于缺getter
方法同樣會導致崩潰道川。