Objective-C關(guān)鍵字@property使用原理探究
@property
@property是OC開發(fā)中常用到的關(guān)鍵字,今天這篇文章就為它做一個較為系統(tǒng)全面的總結(jié)
主要包含內(nèi)容
接下來我會分別解析

存取器方法
一般訪問存取器方法只需要使用.propertyName即可,需要特別指定存取器方法時可通過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:表示自動生成對應(yīng)的
getter和setter方法,即可讀可寫權(quán)限,readwrite是編譯器的默認(rèn)選項。 - readonly:表示只生成
getter,不需要生成setter,即只可讀,不可以修改。
內(nèi)存管理
- strong:指定與目標(biāo)對象存在強(qiáng)(擁有)的關(guān)系,修飾對象的引用計數(shù)會+1,通常用來修飾對象類型,可變集合及可變字符串類型。當(dāng)對象引用計數(shù)為0,即不被任何對象持有,對象就會從內(nèi)存中釋放
- assign:不改變修飾對象的引用計數(shù),通常用來修飾基本數(shù)據(jù)類型(
NSInteger,NSNumber,CGRect,CGFloat等),也是默認(rèn)屬性。需要特別注意的一點是當(dāng)修飾的對象的引用計數(shù)為0對象被銷毀的時候,對象指針不會自動清空成為野指針,后續(xù)再次訪問會產(chǎn)生野指針錯誤:EXC_BAD_ACCESS - copy:對象會在內(nèi)存中拷貝一個副本,副本引用計數(shù)為1。一般用于不可變對象的集合類型,這是為了保證進(jìn)行copy操作的時候生成的都是不可變類型。 copy分深拷貝與淺拷貝,對可變與不可變對象進(jìn)行copy操作結(jié)果如下:
| 源對象類型 | 拷貝方式 | 目標(biāo)對象類型 | 拷貝類型(深|淺) |
|---|---|---|---|
| mutable對象 | copy | 不可變 | 深拷貝 |
| mutable對象 | mutableCopy | 可變 | 深拷貝 |
| immutable對象 | copy | 不可變 | 淺拷貝 |
| immutable對象 | mutableCopy | 可變 | 深拷貝 |
可以總結(jié)以下兩點:
對mutable對象的拷貝都是深拷貝
所有對象的copy結(jié)果都是不可變
weak:弱引用關(guān)系,修飾對象的引用計數(shù)不會增加,當(dāng)修飾對象被銷毀的時候,對象指針會自動置為 nil,主要可以用于避免循環(huán)引用;weak 只能用來修飾對象類型,且是在 ARC 下新引入的修飾詞,只能修飾對象,MRC 下相當(dāng)于使用 assign。
數(shù)據(jù)結(jié)構(gòu)
struct SideTable {
spinlock_t slock;// 用于給原子性操作加鎖
RefcountMap refcnts;// 引用計數(shù)hash表
weak_table_t weak_table;// weak對象指針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;// 存儲 weak 對象信息的 hash 數(shù)組
size_t num_entries;// 數(shù)組中元素的個數(shù),數(shù)組初始化的時候默認(rèn)4個,占用達(dá)到3/4會翻倍擴(kuò)容
uintptr_t mask;// 計數(shù)輔助量
uintptr_t max_hash_displacement;// hash 元素最大偏移值
};
清除weak
對象dealloc的時候,會調(diào)用weak_clear_no_lock函數(shù)將指向該對象的弱引用指針置為nil,具體實現(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 指向的地址,即對象內(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 是一個數(shù)組,存儲所有指向 referent_id 的弱引用
referrers = entry->referrers;
// 弱引用數(shù)組長度
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 中移除對應(yīng)的弱引用的管理容器
weak_entry_remove(weak_table, entry);
}
總結(jié):
當(dāng)一個對象被銷毀時,在dealloc方法內(nèi)部經(jīng)過一系列的函數(shù)調(diào)用棧,通過兩次哈希查找,第一次根據(jù)對象的地址找到它所在的Sidetable,第二次根據(jù)對象的地址在Sidetable的weak_table中找到它的弱引用表。弱引用表中存儲的是對象的地址(作為key)和weak指針地址的數(shù)組(作為value)的映射。weak_clear_no_lock函數(shù)中遍歷弱引用數(shù)組,將指向?qū)ο蟮牡刂返?code>weak變量全都置為nil。
添加weak
一個被聲明為__weak的指針,在經(jīng)過編譯之后。通過objc_initWeak函數(shù)初始化附有__weak修飾符的變量,在變量作用域結(jié)束時通過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é):
一個被標(biāo)記為__weak的指針,在經(jīng)過編譯之后會調(diào)用objc_initWeak函數(shù),objc_initWeak函數(shù)中初始化weak變量后調(diào)用storeWeak。添加weak的過程如下:
經(jīng)過一系列的函數(shù)調(diào)用棧,最終在weak_register_no_lock()函數(shù)當(dāng)中,進(jìn)行弱引用變量的添加,具體添加的位置是通過哈希算法來查找的。如果對應(yīng)位置已經(jīng)存在當(dāng)前對象的弱引用表(數(shù)組),那就把弱引用變量添加進(jìn)去;如果不存在的話,就創(chuàng)建一個弱引用表,然后將弱引用變量添加進(jìn)去。(weak相關(guān)實現(xiàn)較為復(fù)雜后續(xù)的文章會做專門解析)
retain:MRC下使用,ARC下使用strong,用來修飾對象類型,強(qiáng)引用對象,其修飾對象的引用計數(shù)會 +1,不會對對象分配新的內(nèi)存空間。
unsafe_unretained:同weak類似,不會對對象的引用計數(shù) +1,只能用來修飾對象類型,修飾的對象在被銷毀時,其指針不會自動清空,指向的仍然是已銷毀的對象,這時再調(diào)用該指針會產(chǎn)生野指針EXC_BAD_ACCESS錯誤。
原子性
atomic 原子性:系統(tǒng)會自動給生成的 getter/setter 方法進(jìn)行加鎖操作; nonatomic 非原子性:系統(tǒng)不會給自動生成的 getter/setter 方法進(jìn)行加鎖操作; 設(shè)置屬性函數(shù) reallySetProperty(...) 的原子性非原子性實現(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)部實現(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只能對存取器方法加鎖,并不能保障多線程下對對象的其他操作安全。
以上就是Objective-C關(guān)鍵字@property使用原理探究的詳細(xì)內(nèi)容,更多關(guān)于Objective-C關(guān)鍵字@property的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
替代pod update速度慢的lg_pod_plugin安裝使用詳解
這篇文章主要介紹了替代pod update速度慢lg_pod_plugin安裝使用方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
實例解析iOS應(yīng)用多線程開發(fā)中NSthread類的用法
這篇文章主要介紹了iOS應(yīng)用多線程開發(fā)中NSthread類的用法,代碼基于傳統(tǒng)的Objective-C,NSthread類需要的朋友可以參考下2016-02-02
iOS實現(xiàn)點擊狀態(tài)欄自動回到頂部效果詳解
在IOS開發(fā)過程中,經(jīng)常會有這種需求,需要通過點擊狀態(tài)欄返回到頂部,給用戶更好的體驗效果,下面這篇文章給大家詳細(xì)介紹了實現(xiàn)過程,有需要的可以參考借鑒。2016-09-09
IOS 下獲取 rootviewcontroller 的版本不同的問題解決辦法
這篇文章主要介紹了IOS 下獲取 rootviewcontroller 的版本不同的問題解決辦法的相關(guān)資料,希望通過本文能幫助到大家,讓大家遇到這種問題可以解決,需要的朋友可以參考下2017-10-10
iOS左右滑動標(biāo)簽頁導(dǎo)航的設(shè)計
這篇文章主要為大家詳細(xì)介紹了iOS左右滑動標(biāo)簽頁導(dǎo)航的設(shè)計思路,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06

