手撕iOS底層07 -- What is object?

手撕iOS底層07 -- What is object?

前幾章深刻分析了對象的建立過程。本章總結下什麼是對象?即對象的本質前端


0x00 -- Clang介紹

參考官網ios

Clang就是LLVM項目中的一個c家族(C, C++, Objective C/C++, OpenCL, CUDA, and RenderScript)前端編譯器,與GCC同樣,有編譯器的功能。 固然也有其它的特性, 能夠去參考官網瞭解。c++

主要用於底層編譯,將一些oc文件輸出成cpp文件,main.m ---> main.cpp;爲了更好的觀底層的一些結構與實現的邏輯, 方便開發者瞭解底層。objective-c


//一、將 main.m 編譯成 main.cpp
clang -rewrite-objc main.m -o main.cpp

//二、將 ViewController.m 編譯成  ViewController.cpp
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk ViewController.m

//如下兩種方式是經過指定架構模式的命令行,使用xcode工具 xcrun
//三、模擬器文件編譯
- xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 

//四、真機文件編譯
- xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp 
複製代碼

0x01 -- OC對象的本質

main.m文件shell

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end
複製代碼

main.cpp文件,經過clang -rewrite-objc main.m -o main.cpp將編譯出來的main.cpp文件打開後,xcode

//NSObject的定義
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
// NSObject底層編譯
struct NSObject_IMPL {
	Class isa;
};

struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS; // isa
	NSString *_name;
};
複製代碼

LGPerson類在底層編譯成一個結構體LGPerson_IMPL,第一個成員變量struct NSObject_IMPL NSObject_IVARS,至關於NSObjectisa。底層使用這種僞繼承的方式;繼承了父類NSObject的成員isamarkdown

0x02 -- NSObject的isa爲何是class

在前邊的分析中initInstanceIsa中, 初始化isa的類型是一個union isa_t ,即一個聯合體的類型。架構

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h 這裏是一些位域信息
    };
#endif
};

isa = isa_t((uintptr_t)cls);
複製代碼

爲何在上邊的代碼中, NSObjectisaClass類型,app

  • 第一是對外展現爲Class類型 ,從而對外反饋的是類信息。爲了讓開發人員更加明確。
  • 第二是在 Class ISA();的時候,返回isa的時候,作了Class類型強轉
inline Class objc_object::ISA() {
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK); // 強轉成爲Class
#endif
}
複製代碼

總結

  • OC對象本質就是結構體
  • 每一個一個自定義的類中的isa是繼承父類的,直至到繼承自NSObjectisa

0x03 -- objc_setProperty探索

在生成的main.cpp文件中, 在Objective-C代中,把屬性編譯成這樣的iphone

// @property (nonatomic, copy) NSString *nickName;
// ----------------------------
struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name; // 下劃線成員變量
};
// @property (nonatomic, copy) NSString *name;
/* @end */


// @implementation LGPerson
// getter方法
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// setter方法
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
複製代碼

衆所周知, OC中的屬性,會生成一對getter+setter方法,和一個帶下劃線的成員變量

setter方法中,調用了一個objc_setProperty 的方法,在objc-781源碼中,搜索這個方法的實現

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
    if (offset == 0) {
        object_setClass(self, newValue); // 設置isa指向
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue); // 新值retain
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);// 舊值release
}
複製代碼

在這個方法裏真正的去調用了底層RuntimeAPIreallySetProperty的方法。在reallySetProperty主要作了oc屬性setter方法作的事情, 持有新值retain,釋放舊值release


總結objc_setProperty

objc_setProperty在上下層起到承上啓下的做用。 連接🔗上層的setter方法和底層setter方法,本質就是接口

緣由是每一個開發者在上層寫的setter方法,在編譯到底層時, 會產生許多相同做用的方法與成員變量。 佔用沒必要要的空間。增長查找方法的時間。

因此蘋果採用適配器模式,底層適配爲客戶須要的接口, 對上暴露統一接口,供上層setter方法使用,對內調用底層setter方法,相互不受影響,即上層怎麼變化, 下層都是不變,反過來,下層的變化也不回影響到上層。達到上下層隔離的目的。


歡迎大佬留言指正😄,碼字不易,以爲好給個贊👍 有任何表達或者理解失誤請留言交流;共同進步;

相關文章
相關標籤/搜索