OC底層之對象

舉個??

我們使用clang命令轉(zhuǎn)成c++文件

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

通過上述命令獲取 Test1 Test2 相關(guān)的源碼:

初探

上圖代碼看出:

  • OC對象在底層的本質(zhì)就是結(jié)構(gòu)體,結(jié)構(gòu)體中包含了所有屬性方法和父類信息

  • typedef struct objc_object Test1, 可看出Test1objc_object類型的結(jié)構(gòu)體


typedef struct objc_class *Class;

typedef struct objc_object *id;

可看出:

  • Class類實際上是 struct objc_class *結(jié)構(gòu)體指針類型, Class其實就是個別名

  • id實際上是 struct objc_object *結(jié)構(gòu)體指針類型, 這就是為什么我們平常定義id類型不帶 *原因

  • NSObject_IMPL內(nèi)部只有一個成員變量isa

  • @property屬性在底層取消了屬性而是轉(zhuǎn)換成"下劃線+成員變量"以及set, get方法的形式

  • @ interface{}成員變量在底層還是以成員變量的存放, 不會有set, get方法

  • struct NSObject_IMPL NSObject_IVARS其實就是ISA

  • 結(jié)構(gòu)體是可以繼承的, 在C++是可以繼承的, 在C可以偽繼承。這種繼承的方式是直接將NSObject結(jié)構(gòu)體定義為 Test1中的第一個成員變量, 意味著 Test1 擁有 NSObject中的所有成員變量。所以 NSObject_IVARS 等效于 NSObject中的isa

  • 同理,Test2中的第一個成員變量是struct Test1_IMPL Test1_IVARS;

  • OC對象在底層被C語言編程成結(jié)構(gòu)體。而C語言結(jié)構(gòu)體的繼承方式,是每一個結(jié)構(gòu)體第一個屬性都包含父結(jié)構(gòu)體的所有信息。如此,實現(xiàn)了OC類的繼承關(guān)系

所以, Test2的結(jié)構(gòu)簡化后是:


struct Test2_IMPL {

Class isa;

NSString *ivar_name;

NSString *_name;

NSString *_name0;

};

GetterSetter

偏移量定義


通過上述代碼有可看出:

  • NSString *_name; NSString *_name0;是自定義的屬性, 在底層是以成員變量的形式存放的

  • 定義的屬性,底層自動添加了 get 和 set 方法。get 方法名為 _I_類名_屬性名,set 方法名為 _I_類名_set屬性名_,例如:_I_Test1_name _I_Test1_setName_ 就是他們的get set方法

  • Test1 * self, SEL _cmd,這些是隱藏參數(shù),只存在于底層

  • get方法取值return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObj$_SAName));, 實際上也是一種平移取值方法, self(首地址) + OBJC_IVAR_$_TestObj$_SAName(偏移量) = 想要獲取值的地址, 然后通過直接訪問指針地址,返回指針地址的值

  • set方法則是通過調(diào)用objc_setProperty

什么是對象

  • 對象: 所有繼承于objc_object的都叫做對象

  • 實例對象: 即是類對象objc_class實例化出來的

  • isa: 所有對象都有一個isa,指向它的類或元類,里面存儲這類或元類的信息

struct objc_object


對象類型的底層結(jié)構(gòu),首元素是isa(這里涉及到struct結(jié)構(gòu)的內(nèi)存優(yōu)化,我們這里記住結(jié)論。isa在objc_object的首位元素即可)

內(nèi)存優(yōu)化看這里

struct objc_class


從上圖的結(jié)構(gòu)可以看出,在類的內(nèi)存中,首地址表示isasuperclassisa后面,需要內(nèi)存地址偏移8位獲取

深入探索

示例:

image.png
image.png
尋找父類
  • 通過內(nèi)存位移獲取到了實例對象m的類對象Man
  • 根據(jù)類對象objc_class結(jié)構(gòu),可以看出superclassisa之后,isa占8位,所以通過類地址偏移8位獲取到父類Person
  • 先獲取到Personisa0x00000001000089d8

  • 然后內(nèi)存偏移8位,獲取到它的父類NSObject

  • 同上繼續(xù)查找


NSObjet 的父類是nil(0x0000000000000000) over!

isa溯源

元類的定義和創(chuàng)建都由系統(tǒng)控制,由編譯器自動完成,不受我們管理

實例對象的isa來自于類,類也是對象。那類的isa就指向元類

類既然是對象,就需要管理方法、屬性的存儲和歸屬。而這個管理者,就是元類(Meta)

  • 通過獲取Man類的isa,打印出來還是Man,即是Man的元類
  • 繼續(xù)打印Manisa,獲取到Man的根元類NSObject: 0x00000001003720f0,

  • 根元類NSObjectisa則指向自己0x00000001003720f0

  • 通過直接打印NSObject類地址,發(fā)現(xiàn)與上圖中的不一致

  • 而且NSObjectisa也指向上圖的NSObject

  • 所以上圖的NSObject: 0x00000001003720f0根元類

  • 按照上面的方法,獲取根元類0x00000001003720f0的父類

可以看到,根元類的父類isa剛好指向NSObject本類0x0000000100372140

  • 獲取Man元類的父類數(shù)據(jù)

可以看出,Man元類的父類是Person的元類

以上得出結(jié)論如下圖:

Set方法


set方法最終會走到reallySetProperty做處理


objc_setProperty相當于一個承上啟下接口, 上層許多個set方法直接對接llvm, 則llvm需要針對每一個set做對應(yīng)處理, 則會很麻煩。所以蘋果設(shè)置了一個中間層(接口隔離層)objc_setProperty, 令他處理一部分set方法, 保證無論上層怎么變, 傳入llvm的格式不會有變化。主要是達到上下層接口隔離的目的

  • 外部set方法: 個性化定制層(例如setName、setAge等)

  • objc_setProperty:接口隔離層 (將外界信息轉(zhuǎn)化為對內(nèi)存地址和值的操作)

  • reallySetProperty:底層實現(xiàn)層 (賦值和內(nèi)存管理)
LLVM中的objc_setProperty
  • 在llvm源碼中搜索"objc_setProperty"

發(fā)現(xiàn)getSetPropertyFn()方法中調(diào)用CGM.CreateRuntimeFunction(FTy, "objc_setProperty");CGM創(chuàng)建runtime函數(shù)objc_setProperty

Fn: 為function函數(shù)的縮寫

  • 全局搜索getSetPropertyFn()
  • 查詢GetPropertySetFunction

可看到如果case為GetSetProperty和SetPropertyAndExpressionGet會調(diào)用GetPropertySetFunction

其中UseOptimizedSetter(CGM)這個判斷后面標注為// 10.8 and iOS 6.0 code and GC is off即10.8和iOS 6.0代碼,GC關(guān)閉, 所以新版本直接走GetPropertySetFunction

  • 查詢GetSetProperty

IsCopy: 如果屬性修飾符為copy, 直接會調(diào)用objc_setProperty

Retain & !IsAtomic: 如果屬性修飾符為retain, 并且為非原子性nonatomic, 也會調(diào)用objc_setProperty

  • 驗證:
image.png

可看到 copy或者 retain&nonatomic,會調(diào)用objc_setProperty

assgin的話


直接進行偏移后賦值操作

  • retain和copy都是調(diào)用了objc_setProperty。 不同的是objc_setProperty內(nèi)部實現(xiàn)不同(詳看objc4源碼中的objc_setProperty代碼)

  • copy和mutableCopy:是新開辟空間,舊值release;

其他修飾類型:是新值retain,舊值release。

  • strong、assign類型都是直接使用地址進行賦值(通過對象地址偏移相應(yīng)字節(jié)找到屬性地址)

  • 如果在set方法后加入斷點,可以在匯編層看到所有屬性賦值后,會調(diào)用objc_storeStrong。

ARC下, retain和strong都走這里:


retain新值,然后賦值再將舊值release

探索


+ (id)self {

/// 當前類對象

return (id)self;

}

- (id)self {

/// 返回當前實例

return self;

}

+ (Class)class {

/// 返回當前類

return self;

}

- (Class)class {

/// 當前實例對象的類

return object_getClass(self);

}

+ (Class)superclass {

/// 返回當前類的父類

return self->getSuperclass();

}

- (Class)superclass {

/// 返回當前類的父類

return [self class]->getSuperclass();

}

+ (BOOL)isMemberOfClass:(Class)cls {

/// 當前類的元類是否與傳入的類相等

return self->ISA() == cls;

}

- (BOOL)isMemberOfClass:(Class)cls {

/// 當前實例對象的類是否與傳入的類相等

return [self class] == cls;

}

+ (BOOL)isKindOfClass:(Class)cls {

// 循環(huán)判斷

// 元類 vs 傳入類

// 父元類 vs 傳入類

// 根元類 vs 傳入類

// 根類 vs 傳入類

for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {

if (tcls == cls) return YES;

}

return NO;

}

- (BOOL)isKindOfClass:(Class)cls {

// 循環(huán)判斷

// 類 vs 傳入類

// 父類 vs 傳入類

// 根類 vs 傳入類

// nil vs 傳入類

for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {

if (tcls == cls) return YES;

}

return NO;

}

isKindOfClass的底層實現(xiàn):


// Calls [obj isKindOfClass]

BOOL

objc_opt_isKindOfClass(id obj, Class otherClass)

{

#if __OBJC2__

if (slowpath(!obj)) return NO;

// 做了一步obj->getIsa() 獲取isa, 即

// 如果obj是對象,則isa是類,

// 如果obj是類,則isa是元類

Class cls = obj->getIsa();

// 緩存中是否能查找到當前類的isKindOfClass方法

// 找到走if

if (fastpath(!cls->hasCustomCore())) {

// 循環(huán)判斷

// 如果是對象

// 當前類 vs 傳入類

// 父類 vs 傳入類

// 根類 vs 傳入類

// nil vs 傳入類

// 如果是類

// 元類 vs 傳入類

// 父元類 vs 傳入類

// 根元類 vs 傳入類

for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {

if (tcls == otherClass) return YES;

}

return NO;

}

#endif

/// 沒找到,直接發(fā)送消息

return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容