前幾章深刻分析了對象的建立過程。本章總結下什麼是對象?即對象的本質前端
參考官網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
複製代碼
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
,至關於NSObject
的isa
。底層使用這種僞繼承
的方式;繼承了父類NSObject
的成員isa
。markdown
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);
複製代碼
爲何在上邊的代碼中, NSObject
的isa
是Class
類型,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
是繼承父類的,直至到繼承自NSObject
的isa
在生成的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
在上下層起到承上啓下的做用。 連接🔗上層的setter
方法和底層setter
方法,本質就是接口
。緣由是每一個開發者在上層寫的
setter
方法,在編譯到底層時, 會產生許多相同做用的方法與成員變量。 佔用沒必要要的空間。增長查找方法的時間。因此蘋果採用
適配器模式,底層適配爲客戶須要的接口
, 對上暴露統一接口,供上層setter
方法使用,對內調用底層setter
方法,相互不受影響,即上層怎麼變化, 下層都是不變,反過來,下層的變化也不回影響到上層。達到上下層隔離的目的。