Objective-C關(guān)鍵字@property使用原理探究
@property
@property是OC開發(fā)中常用到的關(guān)鍵字,今天這篇文章就為它做一個(gè)較為系統(tǒng)全面的總結(jié)
主要包含內(nèi)容
接下來(lái)我會(huì)分別解析
存取器方法
一般訪問存取器方法只需要使用.propertyName即可,需要特別指定存取器方法時(shí)可通過(guò)getter=getterName與setter=setterName,具體示例如下:
// 指定getter訪問名為isOpen
@property (nonatomic, assign, getter=isOpen) BOOL open;
// 指定setter方法名為setNickName:
@property (nonatomic, copy, setter=setNickName:) NSString *name;
讀寫權(quán)限
- readwrite:表示自動(dòng)生成對(duì)應(yīng)的
getter
和setter
方法,即可讀可寫權(quán)限,readwrite
是編譯器的默認(rèn)選項(xiàng)。 - readonly:表示只生成
getter
,不需要生成setter
,即只可讀,不可以修改。
內(nèi)存管理
- strong:指定與目標(biāo)對(duì)象存在強(qiáng)(擁有)的關(guān)系,修飾對(duì)象的引用計(jì)數(shù)會(huì)+1,通常用來(lái)修飾對(duì)象類型,可變集合及可變字符串類型。當(dāng)對(duì)象引用計(jì)數(shù)為0,即不被任何對(duì)象持有,對(duì)象就會(huì)從內(nèi)存中釋放
- assign:不改變修飾對(duì)象的引用計(jì)數(shù),通常用來(lái)修飾基本數(shù)據(jù)類型(
NSInteger
,NSNumber
,CGRect
,CGFloat
等),也是默認(rèn)屬性。需要特別注意的一點(diǎn)是當(dāng)修飾的對(duì)象的引用計(jì)數(shù)為0對(duì)象被銷毀的時(shí)候,對(duì)象指針不會(huì)自動(dòng)清空成為野指針,后續(xù)再次訪問會(huì)產(chǎn)生野指針錯(cuò)誤:EXC_BAD_ACCESS
- copy:對(duì)象會(huì)在內(nèi)存中拷貝一個(gè)副本,副本引用計(jì)數(shù)為1。一般用于不可變對(duì)象的集合類型,這是為了保證進(jìn)行copy操作的時(shí)候生成的都是不可變類型。 copy分深拷貝與淺拷貝,對(duì)可變與不可變對(duì)象進(jìn)行copy操作結(jié)果如下:
源對(duì)象類型 | 拷貝方式 | 目標(biāo)對(duì)象類型 | 拷貝類型(深|淺) |
---|---|---|---|
mutable對(duì)象 | copy | 不可變 | 深拷貝 |
mutable對(duì)象 | mutableCopy | 可變 | 深拷貝 |
immutable對(duì)象 | copy | 不可變 | 淺拷貝 |
immutable對(duì)象 | mutableCopy | 可變 | 深拷貝 |
可以總結(jié)以下兩點(diǎn):
對(duì)mutable對(duì)象的拷貝都是深拷貝
所有對(duì)象的copy結(jié)果都是不可變
weak:弱引用關(guān)系,修飾對(duì)象的引用計(jì)數(shù)不會(huì)增加,當(dāng)修飾對(duì)象被銷毀的時(shí)候,對(duì)象指針會(huì)自動(dòng)置為 nil
,主要可以用于避免循環(huán)引用;weak
只能用來(lái)修飾對(duì)象類型,且是在 ARC
下新引入的修飾詞,只能修飾對(duì)象,MRC
下相當(dāng)于使用 assign
。
數(shù)據(jù)結(jié)構(gòu)
struct SideTable { spinlock_t slock;// 用于給原子性操作加鎖 RefcountMap refcnts;// 引用計(jì)數(shù)hash表 weak_table_t weak_table;// weak對(duì)象指針hash表 }
/** * The global weak references table. Stores object ids as keys, * and weak_entry_t structs as their values. */ struct weak_table_t { weak_entry_t *weak_entries;// 存儲(chǔ) weak 對(duì)象信息的 hash 數(shù)組 size_t num_entries;// 數(shù)組中元素的個(gè)數(shù),數(shù)組初始化的時(shí)候默認(rèn)4個(gè),占用達(dá)到3/4會(huì)翻倍擴(kuò)容 uintptr_t mask;// 計(jì)數(shù)輔助量 uintptr_t max_hash_displacement;// hash 元素最大偏移值 };
清除weak
對(duì)象dealloc
的時(shí)候,會(huì)調(diào)用weak_clear_no_lock
函數(shù)將指向該對(duì)象的弱引用指針置為nil
,具體實(shí)現(xiàn)如下
// objc-weak.mm /** * Called by dealloc; nils out all weak pointers that point to the * provided object so that they can no longer be used. * * @param weak_table * @param referent The object being deallocated. */ void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) { // 獲得 weak 指向的地址,即對(duì)象內(nèi)存地址 objc_object *referent = (objc_object *)referent_id; // 找到管理 referent 的 entry 容器 weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); // 如果 entry == nil,表示沒有弱引用需要置為 nil,直接返回 if (entry == nil) { /// XXX shouldn't happen, but does with mismatched CF/objc //printf("XXX no entry for clear deallocating %p\n", referent); return; } // zero out references weak_referrer_t *referrers; size_t count; if (entry->out_of_line()) { // referrers 是一個(gè)數(shù)組,存儲(chǔ)所有指向 referent_id 的弱引用 referrers = entry->referrers; // 弱引用數(shù)組長(zhǎng)度 count = TABLE_SIZE(entry); } else { referrers = entry->inline_referrers; count = WEAK_INLINE_COUNT; } // 遍歷弱引用數(shù)組,將所有指向 referent_id 的弱引用全部置為 nil for (size_t i = 0; i < count; ++i) { objc_object **referrer = referrers[i]; if (referrer) { if (*referrer == referent) { *referrer = nil; } else if (*referrer) { _objc_inform("__weak variable at %p holds %p instead of %p. " "This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", referrer, (void*)*referrer, (void*)referent); objc_weak_error(); } } } // 從 weak_table 中移除對(duì)應(yīng)的弱引用的管理容器 weak_entry_remove(weak_table, entry); }
總結(jié):
當(dāng)一個(gè)對(duì)象被銷毀時(shí),在dealloc
方法內(nèi)部經(jīng)過(guò)一系列的函數(shù)調(diào)用棧,通過(guò)兩次哈希查找,第一次根據(jù)對(duì)象的地址找到它所在的Sidetable
,第二次根據(jù)對(duì)象的地址在Sidetable
的weak_table
中找到它的弱引用表。弱引用表中存儲(chǔ)的是對(duì)象的地址(作為key
)和weak
指針地址的數(shù)組(作為value
)的映射。weak_clear_no_lock
函數(shù)中遍歷弱引用數(shù)組,將指向?qū)ο蟮牡刂返?code>weak變量全都置為nil
。
添加weak
一個(gè)被聲明為__weak
的指針,在經(jīng)過(guò)編譯之后。通過(guò)objc_initWeak
函數(shù)初始化附有__weak
修飾符的變量,在變量作用域結(jié)束時(shí)通過(guò)objc_destroyWeak
函數(shù)銷毀該變量。
id obj = [[NSObject alloc] init]; id __weak obj1 = obj; /*----- 編譯 -----*/ id obj1; objc_initWeak(&obj1,obj); objc_destroyWeak(&obj1);
objc_initWeak
函數(shù)調(diào)用棧如下:
// NSObject.mm 1. objc_initWeak 2. storeWeak // objc-weak.mm 3. weak_register_no_lock 4. weak_unregister_no_lock
總結(jié):
一個(gè)被標(biāo)記為__weak
的指針,在經(jīng)過(guò)編譯之后會(huì)調(diào)用objc_initWeak
函數(shù),objc_initWeak
函數(shù)中初始化weak
變量后調(diào)用storeWeak
。添加weak
的過(guò)程如下:
經(jīng)過(guò)一系列的函數(shù)調(diào)用棧,最終在weak_register_no_lock()
函數(shù)當(dāng)中,進(jìn)行弱引用變量的添加,具體添加的位置是通過(guò)哈希算法來(lái)查找的。如果對(duì)應(yīng)位置已經(jīng)存在當(dāng)前對(duì)象的弱引用表(數(shù)組),那就把弱引用變量添加進(jìn)去;如果不存在的話,就創(chuàng)建一個(gè)弱引用表,然后將弱引用變量添加進(jìn)去。(weak相關(guān)實(shí)現(xiàn)較為復(fù)雜后續(xù)的文章會(huì)做專門解析)
retain:MRC
下使用,ARC
下使用strong
,用來(lái)修飾對(duì)象類型,強(qiáng)引用對(duì)象,其修飾對(duì)象的引用計(jì)數(shù)會(huì) +1,不會(huì)對(duì)對(duì)象分配新的內(nèi)存空間。
unsafe_unretained:同weak
類似,不會(huì)對(duì)對(duì)象的引用計(jì)數(shù) +1,只能用來(lái)修飾對(duì)象類型,修飾的對(duì)象在被銷毀時(shí),其指針不會(huì)自動(dòng)清空,指向的仍然是已銷毀的對(duì)象,這時(shí)再調(diào)用該指針會(huì)產(chǎn)生野指針EXC_BAD_ACCESS
錯(cuò)誤。
原子性
atomic
原子性:系統(tǒng)會(huì)自動(dòng)給生成的 getter/setter
方法進(jìn)行加鎖操作; nonatomic
非原子性:系統(tǒng)不會(huì)給自動(dòng)生成的 getter/setter
方法進(jìn)行加鎖操作; 設(shè)置屬性函數(shù) reallySetProperty(...)
的原子性非原子性實(shí)現(xiàn)如下:
if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); }
獲取屬性函數(shù) objc_getProperty(...)
的內(nèi)部實(shí)現(xiàn)如下:
if (offset == 0) { return object_getClass(self); } // Retain release world id *slot = (id*) ((char*)self + offset); if (!atomic) return *slot; // Atomic retain release world spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = objc_retain(*slot); slotlock.unlock(); // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock. return objc_autoreleaseReturnValue(value);
總結(jié)
由上面代碼可見atomic
只能對(duì)存取器方法加鎖,并不能保障多線程下對(duì)對(duì)象的其他操作安全。
以上就是Objective-C關(guān)鍵字@property使用原理探究的詳細(xì)內(nèi)容,更多關(guān)于Objective-C關(guān)鍵字@property的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS開發(fā)之獲取LaunchImage啟動(dòng)圖的實(shí)例
下面小編就為大家分享一篇iOS開發(fā)之獲取LaunchImage啟動(dòng)圖的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12詳解iOS如何讓Lottie使用網(wǎng)絡(luò)資源做動(dòng)畫的實(shí)現(xiàn)
這篇文章主要為大家介紹了iOS如何讓Lottie使用網(wǎng)絡(luò)資源做動(dòng)畫實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02iOS開發(fā)UI篇—xib的簡(jiǎn)單使用實(shí)例
本篇文章主要介紹了iOS開發(fā)UI篇—xib的簡(jiǎn)單使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11iOS 微信分享功能簡(jiǎn)單實(shí)現(xiàn)
本文介紹了iOS 微信分享功能的實(shí)現(xiàn)步驟與方法,具有一定的參考作用。下面跟著小編一起來(lái)看下吧2017-01-01iOS中利用CoreAnimation實(shí)現(xiàn)一個(gè)時(shí)間的進(jìn)度條效果
在iOS中實(shí)現(xiàn)進(jìn)度條通常都是通過(guò)不停的設(shè)置progress來(lái)完成的,這樣的進(jìn)度條適用于網(wǎng)絡(luò)加載(上傳下載文件、圖片等)。下面通過(guò)本文給大家介紹iOS中利用CoreAnimation實(shí)現(xiàn)一個(gè)時(shí)間的進(jìn)度條,需要的的朋友參考下吧2017-09-09MacOS無(wú)法掛載NFS Operation not permitted錯(cuò)誤解決辦法
這篇文章主要介紹了MacOS無(wú)法掛載NFS Operation not permitted錯(cuò)誤解決辦法的相關(guān)資料2017-02-02