前言
在上一篇文章是的時(shí)候,objc_setProperty方法的實(shí)現(xiàn)并沒(méi)有體現(xiàn)strong和weak這兩個(gè)修飾詞,所以這兩個(gè)修飾詞是有另外的實(shí)現(xiàn),而且是在上已層實(shí)現(xiàn)的;copy修飾詞的話,只有copyWithZone和mutableCopyWithZone方法的調(diào)用,沒(méi)有具體實(shí)現(xiàn)。
在看這么多的blog的時(shí)候,上過(guò)當(dāng),吃過(guò)虧。所以還是從源代碼入手去更好理解這三個(gè)修飾詞吧。
類(lèi)的成員變量
分析strong和weak的實(shí)現(xiàn)之前,先看看對(duì)象的成員變量是怎么是進(jìn)行賦值。
為什么呢?因?yàn)閷?duì)象的屬性的setter本質(zhì)就是對(duì)對(duì)象的成員變量進(jìn)行復(fù)制,一般情況下,每一個(gè)對(duì)象的屬性就對(duì)應(yīng)存在一個(gè)對(duì)象的成員變量。所以本質(zhì)上即使在了解成員變量的strong和weak實(shí)現(xiàn)。
成員變量的getter
先看一下成員變量的結(jié)構(gòu)體定義
//Ivar本質(zhì)就是一個(gè)objc_ivar結(jié)構(gòu)體,在objc_ivar結(jié)構(gòu)體記錄對(duì)象成員變量的信息
//這是在runtime.h頭文件中的定義
typedef struct objc_ivar *Ivar;
struct objc_ivar {
//變量名
char * _Nullable ivar_name OBJC2_UNAVAILABLE;
//變量的數(shù)據(jù)類(lèi)型
char * _Nullable ivar_type OBJC2_UNAVAILABLE;
//變量在對(duì)象指針的偏移量,也就是變量存放在內(nèi)存的實(shí)際位置
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
//這是在objc-class-old.mm文件中的定義,和上面的objc_ivar的機(jī)構(gòu)體是一樣的,只是名稱(chēng)不一樣。
struct old_ivar {
char *ivar_name;
char *ivar_type;
int ivar_offset;
#ifdef __LP64__
int space;
#endif
};
//在objc-class-old.mm文件中的定義了這么幾個(gè)宏
//應(yīng)該是做了一個(gè)新舊結(jié)構(gòu)體的映射
#define oldprotocol(proto) ((struct old_protocol *)proto)
#define oldmethod(meth) ((struct old_method *)meth)
#define oldcategory(cat) ((struct old_category *)cat)
#define oldivar(ivar) ((struct old_ivar *)ivar)
#define oldproperty(prop) ((struct old_property *)prop)
//在objc-class-old.mm文件中,這個(gè)函數(shù)是獲取成員變量在對(duì)象的相對(duì)偏移量
ptrdiff_t ivar_getOffset(Ivar ivar) {
return oldivar(ivar)->ivar_offset;
}
//這是在objc-runtime-new中定義的函數(shù)
ptrdiff_t ivar_getOffset(Ivar ivar) {
if (!ivar) return 0;
return *ivar->offset;
}
在獲取成員變量,先了解上面成員變量的結(jié)構(gòu),可以看到在新舊系統(tǒng)中會(huì)有細(xì)微的差異,但是結(jié)構(gòu)體的結(jié)構(gòu)是一樣的。
下面就是成員變量獲取的實(shí)際實(shí)現(xiàn)了,下面就一句一句代碼的分析里面的邏輯
/**
查找類(lèi)成員變量的函數(shù)
cls:要查找的類(lèi)
ivar:要查找的變量,通過(guò)變量的結(jié)構(gòu)體對(duì)這個(gè)變量進(jìn)行描述,包括變量名、變量類(lèi)型和變量的指針便宜量
ivarOffset:變量相對(duì)于對(duì)象所在位置的指針偏移量
memoryManagement:內(nèi)存管理信息
*/
static void
_class_lookUpIvar(Class cls, Ivar ivar, ptrdiff_t& ivarOffset,
objc_ivar_memory_management_t& memoryManagement)
{
ivarOffset = ivar_getOffset(ivar);
// Look for ARC variables and ARC-style weak.
// Preflight the hasAutomaticIvars check
// because _class_getClassForIvar() may need to take locks.
//查找ARC成員變量和ARC內(nèi)存管理的weak
//提前檢測(cè)是不是AutomaticIvars,其實(shí)就是是否開(kāi)啟ARC
//
bool hasAutomaticIvars = NO;
for (Class c = cls; c; c = c->superclass) {
if (c->hasAutomaticIvars()) {
hasAutomaticIvars = YES;
break;
}
}
if (hasAutomaticIvars) {
Class ivarCls = _class_getClassForIvar(cls, ivar);
if (ivarCls->hasAutomaticIvars()) {
// ARC layout bitmaps encode the class's own ivars only.
// Use alignedInstanceStart() because unaligned bytes at the start
// of this class's ivars are not represented in the layout bitmap.
//ARC 只針對(duì)類(lèi)自身的變量輸出位圖編碼
//使用alignedInstanceStart()是因?yàn)轭?lèi)的變量非位對(duì)齊其起始位置,并不代表在輸出位圖上
ptrdiff_t localOffset =
ivarOffset - ivarCls->alignedInstanceStart();
//查找成員變量的指針偏移量是否在類(lèi)的變量輸出位置
if (isScanned(localOffset, class_getIvarLayout(ivarCls))) {
//如果是,那么就是強(qiáng)引用的管理方式,因?yàn)閷儆陬?lèi)自身的變量,并返回
memoryManagement = objc_ivar_memoryStrong;
return;
}
//查找成員變量的指針偏移量是否在弱變量區(qū)域
if (isScanned(localOffset, class_getWeakIvarLayout(ivarCls))) {
//如果是,那么就是弱引用的管理方式,并返回
memoryManagement = objc_ivar_memoryWeak;
return;
}
// Unretained is only for true ARC classes.
//這個(gè)類(lèi)是ARC管理的其他情況就是Unretained的變量
if (ivarCls->isARC()) {
memoryManagement = objc_ivar_memoryUnretained;
return;
}
}
}
//非ARC內(nèi)存管理的,那么就賦值為未知內(nèi)存管理方式
memoryManagement = objc_ivar_memoryUnknown;
}
/**
獲取對(duì)象的成員變量
obj:對(duì)象
ivar:成員變量的描述,包括成員變量的名稱(chēng)、類(lèi)型和偏移量
*/
id object_getIvar(id obj, Ivar ivar)
{
if (!obj || !ivar || obj->isTaggedPointer()) return nil;
//成員變量的指針偏移量,用來(lái)存放查找偏移量結(jié)果
ptrdiff_t offset;
//成員變量的內(nèi)存管理方式,用來(lái)存放查找變量對(duì)應(yīng)的內(nèi)存管理方式
objc_ivar_memory_management_t memoryManagement;
//通過(guò)對(duì)象的類(lèi),去查找成員變量,并且將偏移指針存放在offset,內(nèi)存方式存放在memoryManagement
_class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
//通過(guò)取到成員變量的偏移量,以對(duì)象為起始位置計(jì)算出其實(shí)際位置,獲取對(duì)象指針
id *location = (id *)((char *)obj + offset);
//如果是弱應(yīng)用
if (memoryManagement == objc_ivar_memoryWeak) {
//則在弱應(yīng)用表中找出對(duì)應(yīng)的對(duì)象
return objc_loadWeak(location);
} else {
//否則,直接返回指向的對(duì)象
return *location;
}
}
通過(guò)以上這段代碼,可以得到下面幾點(diǎn)總結(jié):
- ARC內(nèi)存管理是在這一層實(shí)現(xiàn)的
成員變量的setter
/**
對(duì)對(duì)象成員變量進(jìn)行賦值,這是成員變量賦值在runtime層的實(shí)現(xiàn)
obj:需要賦值的對(duì)象
name:成員遍歷那個(gè)的名稱(chēng)
value:性質(zhì)
assumeStrong:需要設(shè)置的內(nèi)存管理方式
*/
static ALWAYS_INLINE
void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
{
//空值判斷
if (!obj || !ivar || obj->isTaggedPointer()) return;
ptrdiff_t offset;
objc_ivar_memory_management_t memoryManagement;
//獲取對(duì)象成員變量的指針偏移量和內(nèi)存管理方式
_class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
//如果內(nèi)存管理方式為未知
if (memoryManagement == objc_ivar_memoryUnknown) {
//而且指定了strong強(qiáng)引用的,修正memoryManagement
if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
//沒(méi)指定為strong強(qiáng)引用的,修正為Unretained
else memoryManagement = objc_ivar_memoryUnretained;
}
//根據(jù)指針偏移量,獲取成員變量指向的對(duì)象指針
id *location = (id *)((char *)obj + offset);
switch (memoryManagement) {
//弱引用,則將新值存放在弱引用weak表
case objc_ivar_memoryWeak: objc_storeWeak(location, value); break;
//強(qiáng)引用,則將新值存放在強(qiáng)引用的strong表
case objc_ivar_memoryStrong: objc_storeStrong(location, value); break;
//Unretained的,則直接賦值
case objc_ivar_memoryUnretained: *location = value; break;
//這種情況不可能發(fā)生
case objc_ivar_memoryUnknown: _objc_fatal("impossible");
}
}
//非strong默認(rèn)的成員變量進(jìn)行賦值
void object_setIvar(id obj, Ivar ivar, id value)
{
return _object_setIvar(obj, ivar, value, false /*not strong default*/);
}
//對(duì)默認(rèn)是strong的成員變量進(jìn)行賦值
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
{
return _object_setIvar(obj, ivar, value, true /*strong default*/);
}
/**
對(duì)對(duì)象成員變量進(jìn)行賦值,這是成員變量賦值在runtime層的實(shí)現(xiàn)
obj:需要賦值的對(duì)象
name:成員遍歷那個(gè)的名稱(chēng)
value:性質(zhì)
assumeStrong:需要設(shè)置的內(nèi)存管理方式
*/
static ALWAYS_INLINE
Ivar _object_setInstanceVariable(id obj, const char *name, void *value,
bool assumeStrong)
{
Ivar ivar = nil;
if (obj && name && !obj->isTaggedPointer()) {
//通過(guò)給定的名字,查找成員變量
if ((ivar = _class_getVariable(obj->ISA(), name))) {
_object_setIvar(obj, ivar, (id)value, assumeStrong);
}
}
return ivar;
}
//給對(duì)象成員變量進(jìn)行復(fù)制
Ivar object_setInstanceVariable(id obj, const char *name, void *value)
{
return _object_setInstanceVariable(obj, name, value, false);
}
//給對(duì)象用strong默認(rèn)修飾的成員變量進(jìn)行賦值
Ivar object_setInstanceVariableWithStrongDefault(id obj, const char *name,
void *value)
{
return _object_setInstanceVariable(obj, name, value, true);
}
由上面這段代碼可以看出來(lái),可以總結(jié)出來(lái)下面幾點(diǎn):
- 所有對(duì)成員變量的賦值,都是通過(guò)調(diào)用_object_setIvar函數(shù)實(shí)現(xiàn),_object_setIvar函數(shù)是具體實(shí)現(xiàn),通過(guò)參數(shù)區(qū)分功能
- 對(duì)象和對(duì)象的成員變量是放在一片連續(xù)的內(nèi)存塊上面的,在編譯期在類(lèi)中已經(jīng)確定成員變量相對(duì)于對(duì)象的的位置
- weak和strong修飾的成員變量,分別存放在不同的表中
- Unretained修飾成員變量只做賦值操作
strong修飾詞
在上面成員變量的setter上可以看到,對(duì)于用strong修飾的成員變量的賦值,是調(diào)用objc_storeStrong函數(shù)來(lái)實(shí)現(xiàn)的,那么現(xiàn)在看看objc_storeStrong的具體實(shí)現(xiàn)是怎樣的
void
objc_storeStrong(id *location, id obj)
{
//將*location指向的對(duì)象賦值給prev
id prev = *location;
//如果新值和原來(lái)的值一致,則返回,無(wú)需繼續(xù)操作
if (obj == prev) {
return;
}
//對(duì)新值引用計(jì)數(shù)加一
objc_retain(obj);
//將對(duì)象指針指向新值
*location = obj;
//釋放舊值
objc_release(prev);
}
從源代碼上看,對(duì)于strong修飾的成員變量操作比較簡(jiǎn)單,整個(gè)過(guò)程就是對(duì)新值引用計(jì)數(shù)加一,和對(duì)舊值進(jìn)行釋放
weak修飾詞
strong修飾詞的成員變量賦值比較簡(jiǎn)單,那么現(xiàn)在看看objc_storeWeak的源代碼,看看objc_storeWeak是怎么工作的
/**
localtion : 對(duì)象指針
nebObj:新值
*/
static id
storeWeak(id *location, objc_object *newObj)
{
assert(haveOld || haveNew);
//根據(jù)haveNew參數(shù)判斷是否有新值,如果有新值,而且新值指針指向的對(duì)象為空,則提示
if (!haveNew) assert(newObj == nil);
Class previouslyInitializedClass = nil;
//舊值變量,用于存放舊值
id oldObj;
//舊值的weak引用表,用于記錄該值的被引用情況
SideTable *oldTable;
//新值的weak引用表,用于記錄新值的被引用情況
SideTable *newTable;
/**
需要同時(shí)獲取舊值和新值的鎖
為了解決鎖定地址阻止鎖的順序問(wèn)題
如果舊值正在修改,那么跳回retry重新做一遍流程
*/
retry:
if (haveOld) {
//如果haveOld的參數(shù)為真,那么根據(jù)location的對(duì)象指針獲取舊值引用
oldObj = *location;
//則根據(jù)oldObj對(duì)象在全局的一個(gè)hash表獲取SideTables類(lèi)型的引用表
oldTable = &SideTables()[oldObj];
} else {
//否則將舊引用表置空
oldTable = nil;
}
if (haveNew) {
//根據(jù)haveNew為真,則根據(jù)新值的對(duì)象在全局的一個(gè)hash表獲取SideTables類(lèi)型的引用表
newTable = &SideTables()[newObj];
} else {
//否則將新引用表置空
newTable = nil;
}
//同時(shí)對(duì)新引用表和舊引用表加鎖
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
//如果haveOld為真,而且location指針指向的對(duì)象并不是oldObj
if (haveOld && *location != oldObj) {
//那么可能舊值已經(jīng)更改,解鎖兩個(gè)表,然后重試
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
/**
通過(guò)保證沒(méi)有一個(gè)弱引用的對(duì)象是沒(méi)有一個(gè)沒(méi)有initialized的isa
來(lái)打斷弱引用架構(gòu)和+initialize架構(gòu)間的死鎖
*/
if (haveNew && newObj) {
//獲取新值對(duì)象的元類(lèi)對(duì)象
Class cls = newObj->getIsa();
//如果新值的元類(lèi)對(duì)象和之前初始化的不一致,且元類(lèi)未初始化
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
//解鎖兩個(gè)表
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
//初始化新值對(duì)象的元類(lèi)對(duì)象
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
previouslyInitializedClass = cls;
//跳轉(zhuǎn)到重試
goto retry;
}
}
if (haveOld) {
//在舊值的weak表中清除舊值,如果weak表為空,同時(shí)在entry中刪除weak表
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (haveNew) {
/**
將新值的weak表添加在entry中,并且將新值注冊(cè)到weak表中
weak_register_no_lock如果返回nil,則表示弱引用存放被拒絕了
1.類(lèi)中實(shí)現(xiàn)了allowsWeakReference,并且返回NO,表示不允許弱引用
2.該對(duì)象正在調(diào)用deallocating進(jìn)行釋放
*/
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
//同時(shí)將是弱引用的標(biāo)記寫(xiě)在對(duì)象的refcount table的表中
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
//將location對(duì)象指針指向新值
*location = (id)newObj;
}
else {
//沒(méi)有性質(zhì),什么都不做
}
//兩個(gè)表同時(shí)解鎖
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
//返回新值對(duì)象
return (id)newObj;
}
/**
這個(gè)函數(shù)存放一個(gè)新值到一個(gè)使用__weak修飾的變量
可以在任何地方指定一個(gè)__weak變量
location:弱引用自身的內(nèi)存地址
newObj:這個(gè)弱引用指針需要指向的值
返回: 返回新值
*/
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);
}
通過(guò)上面的分析,weak修飾的成員變量的賦值,比strong修飾的成員變量賦值得多了??梢钥偨Y(jié)一下:
- objc_storeWeak函數(shù)不止是用來(lái)對(duì)對(duì)象成員變量的賦值,還包括所有使用__weak修飾符修飾的變量的賦值,以后還需要繼續(xù)看回這個(gè)方法
- 有一個(gè)全局的weak表存放在所有指向weak修飾的對(duì)象的指針的的指針,這個(gè)指針是該對(duì)象使用了weak修飾對(duì)象的引用的指針
- 在對(duì)weak修飾的變量進(jìn)行賦值的時(shí)候,會(huì)將舊值的weak表釋放掉,并對(duì)創(chuàng)建新值的weak表,也就是說(shuō),在成員變量中大量使用weak變量的話,會(huì)一定程度上占用cpu的資源,會(huì)有效率損耗
- 在賦值過(guò)成功有加鎖和解鎖過(guò)程,所以整個(gè)賦值過(guò)程是線程安全的
后記
在了解strong和weak修飾的成員賦值的過(guò)程,發(fā)現(xiàn)strong修飾的成員變量只是增加所持有對(duì)象的循環(huán)引用,而weak修飾的成員變量,則相對(duì)復(fù)雜很多。在了解過(guò)程中,可能還是會(huì)有理解偏差,需要多次閱讀代碼再補(bǔ)充。
還有一個(gè)就是要盡快弄好一個(gè)High Sierra來(lái)打斷點(diǎn)調(diào)試真實(shí)的運(yùn)行過(guò)程,現(xiàn)在只是在源代碼上面的分析理解。
后記的后記
終于將系統(tǒng)降級(jí)到High Sierra了,通過(guò)objc的源代碼進(jìn)行調(diào)試,證明我的理解并沒(méi)有太大的差異。
weak修飾的成員變量進(jìn)行賦值的調(diào)用關(guān)系如下

strong修飾的成員變量進(jìn)行賦值的調(diào)用關(guān)系如下

終于正式了自己測(cè)瞎猜,哈哈。開(kāi)心!