object-c中什么是對(duì)象?看這篇就夠了!!聘裁!

舉個(gè)??

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

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

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

初探


上圖代碼看出:

  • OC對(duì)象在底層的本質(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類實(shí)際上是 struct objc_class *結(jié)構(gòu)體指針類型, Class其實(shí)就是個(gè)別名

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

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

  • @property屬性在底層取消了屬性而是轉(zhuǎn)換成"下劃線+成員變量"以及set, get方法的形式
  • @ interface{}成員變量在底層還是以成員變量的存放, 不會(huì)有set, get方法
  • struct NSObject_IMPL NSObject_IVARS其實(shí)就是ISA
  • 結(jié)構(gòu)體是可以繼承的, 在C++是可以繼承的, 在C可以偽繼承。這種繼承的方式是直接將NSObject結(jié)構(gòu)體定義為 Test1中的第一個(gè)成員變量, 意味著 Test1 擁有 NSObject中的所有成員變量。所以 NSObject_IVARS 等效于 NSObject中的isa
  • 同理,Test2中的第一個(gè)成員變量是struct Test1_IMPL Test1_IVARS;
  • OC對(duì)象在底層被C語言編程成結(jié)構(gòu)體乎完。而C語言結(jié)構(gòu)體的繼承方式摩桶,是每一個(gè)結(jié)構(gòu)體第一個(gè)屬性都包含父結(jié)構(gòu)體的所有信息。如此,實(shí)現(xiàn)了OC類的繼承關(guān)系

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

struct Test2_IMPL {
    Class isa;
    NSString *ivar_name;
    NSString *_name;
    NSString *_name0;
};

GetterSetter


偏移量定義



通過上述代碼有可看出:

  • NSString *_name; NSString *_name0;是自定義的屬性, 在底層是以成員變量的形式存放的
  • 定義的屬性,底層自動(dòng)添加了 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));, 實(shí)際上也是一種平移取值方法, self(首地址) + OBJC_IVAR_$_TestObj$_SAName(偏移量) = 想要獲取值的地址, 然后通過直接訪問指針地址串前,返回指針地址的值
  • set方法則是通過調(diào)用objc_setProperty

什么是對(duì)象

  • 對(duì)象: 所有繼承于objc_object的都叫做對(duì)象
  • 實(shí)例對(duì)象: 即是類對(duì)象objc_class實(shí)例化出來的
  • isa: 所有對(duì)象都有一個(gè)isa,指向它的類或元類,里面存儲(chǔ)這類或元類的信息
struct objc_object

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

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

struct objc_class
576e8943ef4947f797f5a912c1ede210-304104.png

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

深入探索

示例:



尋找父類
  • 通過內(nèi)存位移獲取到了實(shí)例對(duì)象m的類對(duì)象Man
  • 根據(jù)類對(duì)象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)控制,由編譯器自動(dòng)完成简十,不受我們管理
實(shí)例對(duì)象的isa來自于類橙喘,類也是對(duì)象。那類的isa就指向元類
類既然是對(duì)象半沽,就需要管理方法、屬性的存儲(chǔ)和歸屬。而這個(gè)管理者,就是元類(Meta)

  • 通過獲取Man類的isa,打印出來還是Man,即是Man的元類

  • 繼續(xù)打印Manisa,獲取到Man的根元類NSObject: 0x00000001003720f0,

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

    3e92714ded3a0957ba5cb946fefa81bd-65049.png

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

  • 而且NSObjectisa也指向上圖的NSObject

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

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

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

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

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

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


Set方法

set方法最終會(huì)走到reallySetProperty做處理

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

  • 外部set方法: 個(gè)性化定制層(例如setName颊乘、setAge等)
  • objc_setProperty:接口隔離層 (將外界信息轉(zhuǎn)化為對(duì)內(nèi)存地址和值的操作)
  • reallySetProperty:底層實(shí)現(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會(huì)調(diào)用GetPropertySetFunction

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

  • 查詢GetSetProperty



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

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

  • 驗(yàn)證:



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

assign的話

直接進(jìn)行偏移后賦值操作

  • retain和copy都是調(diào)用了objc_setProperty。 不同的是objc_setProperty內(nèi)部實(shí)現(xiàn)不同(詳看objc4源碼中的objc_setProperty代碼)
  • copy和mutableCopy:是新開辟空間渠概,舊值release状共;
    其他修飾類型:是新值retain,舊值release舶吗。
  • strong征冷、assign類型都是直接使用地址進(jìn)行賦值(通過對(duì)象地址偏移相應(yīng)字節(jié)找到屬性地址)
  • 如果在set方法后加入斷點(diǎn),可以在匯編層看到所有屬性賦值后誓琼,會(huì)調(diào)用objc_storeStrong检激。

ARC下, retain和strong都走這里:
[圖片上傳失敗...(image-c94f02-1729845469374)]
retain新值,然后賦值再將舊值release

測(cè)試


+ (id)self {
    /// 當(dāng)前類對(duì)象
    return (id)self;
}

- (id)self {
    /// 返回當(dāng)前實(shí)例
    return self;
}

+ (Class)class {
    /// 返回當(dāng)前類
    return self;
}

- (Class)class {
    /// 當(dāng)前實(shí)例對(duì)象的類
    return object_getClass(self);
}


+ (Class)superclass {
    /// 返回當(dāng)前類的父類
    return self->getSuperclass();
}

- (Class)superclass {
    /// 返回當(dāng)前類的父類
    return [self class]->getSuperclass();
}

+ (BOOL)isMemberOfClass:(Class)cls {
    /// 當(dāng)前類的元類是否與傳入的類相等
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    /// 當(dāng)前實(shí)例對(duì)象的類是否與傳入的類相等
    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的底層實(shí)現(xiàn):

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    // 做了一步obj->getIsa() 獲取isa, 即
   // 如果obj是對(duì)象,則isa是類腹侣,
   // 如果obj是類叔收,則isa是元類
    Class cls = obj->getIsa();
    // 緩存中是否能查找到當(dāng)前類的isKindOfClass方法
    // 找到走if
    if (fastpath(!cls->hasCustomCore())) {
        // 循環(huán)判斷
        // 如果是對(duì)象
        // 當(dāng)前類 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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市傲隶,隨后出現(xiàn)的幾起案子饺律,更是在濱河造成了極大的恐慌,老刑警劉巖跺株,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蓝晒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡帖鸦,警方通過查閱死者的電腦和手機(jī)芝薇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來作儿,“玉大人洛二,你說我怎么就攤上這事」ッ蹋” “怎么了晾嘶?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)娶吞。 經(jīng)常有香客問我垒迂,道長(zhǎng),這世上最難降的妖魔是什么妒蛇? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任机断,我火速辦了婚禮,結(jié)果婚禮上绣夺,老公的妹妹穿的比我還像新娘吏奸。我一直安慰自己,他們只是感情好陶耍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布奋蔚。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泊碑。 梳的紋絲不亂的頭發(fā)上坤按,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音馒过,去河邊找鬼臭脓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沉桌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播算吩,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼留凭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了偎巢?” 一聲冷哼從身側(cè)響起蔼夜,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎压昼,沒想到半個(gè)月后求冷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窍霞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年匠题,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片但金。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡韭山,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冷溃,到底是詐尸還是另有隱情钱磅,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布似枕,位于F島的核電站盖淡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凿歼。R本人自食惡果不足惜褪迟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望答憔。 院中可真熱鬧牵咙,春花似錦、人聲如沸攀唯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侯嘀。三九已至另凌,卻和暖如春谱轨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吠谢。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工土童, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人工坊。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓献汗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親王污。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罢吃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容