對象的分類
- 實(shí)例對象;
- 類對象;
- 元類對象;
1. 實(shí)例對象(instance)
每次alloc
的都是一個新的對象, 地址也不相同;
每個實(shí)例對象中包含的信息有
-
isa
指針(指向類對象) - 其他的成員變量(成員變量的值信息之類, 因?yàn)槊總€示例對象的同一個成員變量值可能不同)
- 內(nèi)存中存放在堆區(qū);
///實(shí)例對象
Model *obj1 = [[Model alloc] init];
Model *obj2 = [[Model alloc] init];
Model *obj3 = [[Model alloc] init];
NSLog(@"實(shí)例對象: obj1: %p, obj2: %p, obj3: %p", obj1, obj2, obj3);
2020-06-18 10:16:32.152935+0800 Instance對象的本質(zhì)[3695:54819] 實(shí)例對象: obj1: 0x600001740ed0, obj2: 0x600001740ea0, obj3: 0x600001740eb0
通過源碼驗(yàn)證
///Model為繼承自 NSObject 的類
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Model : NSObject {
@public
int _age;
double _height;
}
@property (nonatomic, assign) int count;
@end
NS_ASSUME_NONNULL_END
///.m 實(shí)現(xiàn)
#import "Model.h"
@implementation Model
@end
通過指令將Model.m轉(zhuǎn)換為C++文件后可以得到;
Model 的實(shí)例對象存儲結(jié)構(gòu)
struct Model_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
double _height;
int _count;
};
==>
struct NSObject_IMPL {
Class isa;
};
===>所以 Model的結(jié)構(gòu)即:
struct Model_IMPL {
Class isa;///isa 指針
int _age;///成員變量
double _height;///成員變量
int _count;///成員變量(類中的屬性=成員變量+setter+getter)
};
2. 類對象(Class)
一個類的類對象無論何種方式獲取只有一個, 地址始終是同一個;
每個類對象中包含的信息有
-
isa
指針(指向元類對象) -
superclass
指針(指向父類的類對象, 基類的superclass
指向nil
) - 類的屬性方法信息
@property
, 類的實(shí)例方法(-號方法) - 類的協(xié)議信息, 類的成員變量信息
ivar
(注意: 此處的成員變量信息不是值, 而是成員變量類型/名字之類的通用描述信息) - 內(nèi)存中存放在代碼段;
///類對象
Class objClass1 = [obj1 class];
Class objClass2 = [obj2 class];
Class objClass3 = [NSObject class];
Class objClass4 = [[[[[NSObject class] class] class] class] class];
NSLog(@"類對象: objClass1: %p, objClass2: %p, objClass3: %p, objClass4: %p", objClass1, objClass2, objClass3, objClass4);
2020-06-18 10:16:32.153058+0800 Instance對象的本質(zhì)[3695:54819] 類對象: objClass1: 0x7fff89c1ed00, objClass2: 0x7fff89c1ed00, objClass3: 0x7fff89c1ed00, objClass4: 0x7fff89c1ed00
///通過object_getClass獲取對象
//入?yún)?shí)例對象獲得類對象
//入?yún)㈩悓ο螳@取元類對象
//源碼可以得到原因, 因?yàn)槭峭ㄟ^ isa 指針去尋找的;
Class metaClass11 = object_getClass(obj1);
Class metaClass22 = object_getClass(obj2);
Class metaClass33 = object_getClass(obj3);
NSLog(@"通過實(shí)例對象獲取元類對象: metaClass11: %p, metaClass22: %p, metaClass33: %p", metaClass11, metaClass22, metaClass33);
2020-06-18 10:16:32.153165+0800 Instance對象的本質(zhì)[3695:54819] 通過實(shí)例對象獲取元類對象: metaClass11: 0x7fff89c1ed00, metaClass22: 0x7fff89c1ed00, metaClass33: 0x7fff89c1ed00
objc 源碼看類對象的結(jié)構(gòu)
struct objc_object {
private:
isa_t isa;
...
}
===>
struct objc_class : objc_object {
///首先存放的是 isa 指針
// Class ISA;
///存放 superclass 指針
Class superclass;
///方法緩存
cache_t cache; // formerly cache pointer and vtable
/*
具體的類信息, 看注釋,這個東西的得來是靠class_rw_t 加上自定義的 rr/alloc得來的;
class_rw_t: 可以理解為 class_readwrite_table;
class_ro_t: 可以理解為 class_readonly_table;
*/
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
void setInfo(uint32_t set) {
ASSERT(isFuture() || isRealized());
data()->setFlags(set);
}
void clearInfo(uint32_t clear) {
ASSERT(isFuture() || isRealized());
data()->clearFlags(clear);
}
...
}
===>
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
...
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>();
} else {
return extAlloc(v.get<const class_ro_t *>());
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
/*
存儲著一些信息
*/
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>()->ro;
}
return v.get<const class_ro_t *>();
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>()->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
///方法列表
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
}
}
///屬性列表
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->properties;
} else {
return property_array_t{v.get<const class_ro_t *>()->baseProperties};
}
}
///協(xié)議列表
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
}
}
};
===>
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
///成員變量
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...
}
總結(jié)起來類對象中的存儲結(jié)構(gòu)大致為
struct objc_class : objc_object {
///首先存放的是 isa 指針
Class isa;
///存放 superclass 指針
Class superclass;
///方法緩存
cache_t cache;
///方法列表
method_array_t methods()
///屬性列表
property_array_t properties()
///協(xié)議列表
protocol_array_t protocols()
///成員變量
const ivar_list_t * ivars;
....
}
===>
元類對象的存儲結(jié)構(gòu)跟類的存儲結(jié)構(gòu)一樣, 不過有一些部分是 null;
3. 元類對象(Meta-Class)
通過object_getClass
方法, 入?yún)㈩悓ο螳@取到的是元類對象; 每個類的元類對象內(nèi)存中只有一個, 地址始終同一份; meta-class
跟class
的內(nèi)存結(jié)構(gòu)一樣;
每個元類對象中包含的信息有
- isa 指針(指向基類元類對象,
NSObject
元類對象) - superclass 指針(指向父類的元類對象)
特殊:基類的元類對象指向基類的類對象
- 類的方法(+號方法)
///通過類對象獲取元類對象
Class metaClass1 = object_getClass(objClass1);
Class metaClass2 = object_getClass(objClass2);
Class metaClass3 = object_getClass(objClass3);
Class metaClass4 = object_getClass(objClass4);
NSLog(@"通過類對象獲取元類對象: metaClass1: %p, metaClass2: %p, metaClass3: %p, metaClass4: %p", metaClass1, metaClass2, metaClass3, metaClass4);
///是否是元類對象
BOOL ismeta1 = class_isMetaClass(objClass1);
BOOL ismeta2 = class_isMetaClass(objClass2);
BOOL ismeta3 = class_isMetaClass(metaClass1);
BOOL ismeta4 = class_isMetaClass(metaClass2);
NSLog(@"是否是元類: objClass1: %ld, objClass2: %ld, metaClass1: %ld, metaClass2: %ld", (long)ismeta1, (long)ismeta2, (long)ismeta3, (long)ismeta4);
2020-06-18 10:16:32.153275+0800 Instance對象的本質(zhì)[3695:54819] 通過類對象獲取元類對象: metaClass1: 0x7fff89c1ecd8, metaClass2: 0x7fff89c1ecd8, metaClass3: 0x7fff89c1ecd8, metaClass4: 0x7fff89c1ecd8
2020-06-18 10:16:32.153358+0800 Instance對象的本質(zhì)[3695:54819] 是否是元類: objClass1: 0, objClass2: 0, metaClass1: 1, metaClass2: 1
4. Class object_getClass(id obj)方法
Class object_getClass(id obj)
方法;
通過 objc4
源碼可以查到其實(shí)現(xiàn), 是通過入?yún)⒌?isa
指針去獲取返回內(nèi)容, 對象之間的 isa
指向關(guān)系如上;
- 實(shí)例對象的 isa 指向類對象, 所以入?yún)?shí)例對象, 返回類對象;
- 類對象的 isa 指向元類對象, 所以入?yún)㈩悓ο? 返回元類對象;
/***********************************************************************
* object_getClass.
* Locking: None. If you add locking, tell gdb (rdar://7516456).
**********************************************************************/
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
5. objc_getClass(const char * _Nonnull name)方法
objc_getClass(const char * _Nonnull name)
入?yún)⒁粋€字符串嘗試返回一個類; 如果可以映射到則返回類, 如果不能則返回 nil;
/***********************************************************************
* objc_getClass. Return the id of the named class. If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
Class objc_getClass(const char *aClassName)
{
if (!aClassName) return Nil;
// NO unconnected, YES class handler
return look_up_class(aClassName, NO, YES);
}
6. 調(diào)用方法的流程(簡單流程)
子類調(diào)用父類實(shí)例/類方法的流程;
類對象的superclass
指針指向父類的類對象;
元類對象的superclass
指針指向父類的元類對象;
- 調(diào)用方法的代碼編譯后變成如下
注意:發(fā)送的消息只是確定方法名字, 并不指定是"+"號方法或者"-"號方法;
((void (*)(id, SEL))(void *)objc_msgSend)((id)sonModel, sel_registerName("ModelInstanceMethod"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SonModel"), sel_registerName("ModelClassMethod"));
不考慮[動態(tài)解析]和[消息轉(zhuǎn)發(fā)]情況下調(diào)用方法流程為; 完整的流程在objc_msgSend();
-
[sonModel ModelInstanceMethod];
調(diào)用實(shí)例方法流程為, 首先查找緩存, 如果沒有則通過實(shí)例對象的isa
指針查找到對應(yīng)的類對象, 然后查找相應(yīng)的實(shí)例方法, 如果能找到則調(diào)用, 如果不能找到, 則通過類對象的superclass
指針查找父類的類對象, 然后查找相應(yīng)的實(shí)例方法, 找到基類的類對象仍然沒有找到; 如果沒實(shí)現(xiàn)動態(tài)解析
和消息轉(zhuǎn)發(fā)
則拋出錯誤未找到實(shí)例方法的實(shí)現(xiàn); xcode中代碼: unrecognized selector sent to instance
; -
[SonModel ModelClassMethod];
調(diào)用類方法流程為, 首先查找緩存,如果沒有, 通過類對象的isa
指針查找到對應(yīng)的元類對象, 然后查找相應(yīng)的類方法, 如果能找到則調(diào)用, 如果不能找到, 則通過元類對象的superclass
指針查找父類的元類類對象, 然后查找相應(yīng)的類方法, 找到基類的元類對象仍然沒有找到, 則去基類的類對象中查找, 如果仍然沒有; 如果沒實(shí)現(xiàn)動態(tài)解析
和消息轉(zhuǎn)發(fā)
則 拋出錯誤未找到類方法的實(shí)現(xiàn)(默認(rèn)認(rèn)為沒有同名實(shí)例方法); xcode 中代碼: unrecognized selector sent to class
;
特殊情況詳見 7.isa 和 superclass 的指向
SonModel *sonModel = [[SonModel alloc] init];
[sonModel ModelInstanceMethod];
[SonModel ModelClassMethod];
///父類
@interface Model : NSObject {
@public
int _age;
double _height;
}
- (void)ModelInstanceMethod;
+ (void)ModelClassMethod;
@end
///子類
@interface SonModel : Model {
int _count;
}
- (void)SonModelInstanceMethod;
+ (void)SonModelClassMethod;
@end
7. isa 和 superclass 的指向
isa 和 superclass 的指向 如下
接著知識點(diǎn)6解釋下那種特殊情況: 如果調(diào)用類方法在基類的元類對象中仍然沒有找到, 則會基類的元類對象的
superclass
指針去基類的類對象中查找同名的實(shí)例方法, 如果有同名的實(shí)例方法則使用; 如果沒有則拋出錯誤;具體測試代碼如下
為
NSObject
添加Catogary
如下
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Method)
+ (void)test;
@end
NS_ASSUME_NONNULL_END
#import "NSObject+Method.h"
@implementation NSObject (Method)
- (void)test {
NSLog(@"NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為%p", self);
}
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Model : NSObject
+ (void)test;
@end
NS_ASSUME_NONNULL_END
#import "Model.h"
@implementation Model
@end
運(yùn)行結(jié)果如下, 由此可以驗(yàn)證如上結(jié)論;
NSLog(@"NSOject類對象地址: %p, Model類對象地址: %p", [NSObject class], [Model class]);
[NSObject test];
[Model test];
2020-06-18 21:02:59.282460+0800 Instance對象的本質(zhì)[2833:21502] NSOject類對象地址: 0x101ec5f38, Model類對象地址: 0x100e2b640
2020-06-18 21:02:59.282578+0800 Instance對象的本質(zhì)[2833:21502] NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為0x101ec5f38
2020-06-18 21:02:59.282664+0800 Instance對象的本質(zhì)[2833:21502] NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為0x100e2b640
補(bǔ)充:
-
superclass
指針的地址直接就是相應(yīng)父類對象的地址; -
isa
的值并不是直接指向類/元類對象的地址, 從64位系統(tǒng)開始isa
的值進(jìn)行一次位運(yùn)算后才能得出相應(yīng)的類/元類對象的地址;
實(shí)例對象isa & ISA_MASK
= 類對象地址;
類對象isa & ISA_MASK
= 元類對象地址;
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL//arm64架構(gòu)使用這個值: 0x0000000ffffffff8
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL//x86_64架構(gòu)使用這個值: 0x00007ffffffffff8
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
通過指令將OC文件轉(zhuǎn)換為C++文件
指令: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件.m -o 文件-arm64.cpp
如果需要鏈接其他框架, 使用-framework參數(shù); 例: -framework UIKit
參考文章和下載鏈接
Apple 一些源碼的下載地址
有理解錯誤地方請不吝賜教