我們平時撰寫的Objective-C代碼,底層實作其實都是C\C++代碼,所以Objective-C的面向物件都是基于C\C++的資料結構實作的

OC物件的本質
Objective-C的物件、類主要是基于C\C++的結構體實作的
通過下面的命令可以將OC代碼轉換為C++代碼來查看
clang -rewrite-objc OC源檔案 -o 輸出的CPP檔案
由于Clang會根據不同平臺轉換的C++代碼有所差異,所以針對iOS平臺用下面的命令來轉換
// 意為:通過Xcode運行iPhone平臺arm64架構,重寫OC檔案到C++檔案
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源檔案 -o 輸出的CPP檔案
如果需要鏈接其他框架,使用-framework引數,比如-framework UIKit
凡是繼承自NSObject的物件,都會自帶一個型別是Class的isa的成員變數,將其轉成C++,就可以看到NSObject本質上是一個叫做NSObject_IMPL的結構體,其成員變數isa本質上也是一個指向objc_class結構體的指標


OC物件的記憶體布局
一個OC物件在記憶體中的布局是這樣的,系統會在堆中開辟一塊記憶體空間存放該物件,這塊空間里還包含成員變數和isa指標,然后堆疊里的區域變數指向這塊存盤空間的地址


OC物件的記憶體占用大小
系統會給NSObject物件自動分配16個位元組的記憶體,而NSObject物件實際只占用了8個位元組的記憶體,這8個位元組的大小就是成員變數isa指標的大小,多余的8個位元組是系統為了記憶體對齊而分配的
// 獲取實體物件的記憶體大小,實際是獲取物件成員變數的記憶體大小
#import <objc/runtime.h>class_getInstanceSize([NSObject class]);// 獲取實體物件的記憶體大小,實際是獲取系統真正分配了多少記憶體#import <malloc/malloc.h>malloc_size((__bridge const void *)obj);
NSObject *obj = [[NSObject alloc] init];
// 獲得NSObject實體物件的成員變數所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 獲得obj指標所指向記憶體的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
驗證方法
1.原始碼驗證
下載蘋果開源框架 https://opensource.apple.com/tarballs/objc4/
選擇最大版本下載

在頭檔案objc-runtime-new.h中找到對應代碼
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
// 只要小于16個位元組都會被賦值16
if (size < 16) size = 16;
return size;
}
2.記憶體驗證
運行Xcode,選擇Debug->Debug Workflow -> View Memory查看記憶體資料

輸入obj的記憶體地址可以看到只有前8個位元組有值,但已經分配了16個位元組的記憶體空間

3.LLDB列印驗證
利用LLDB的memory read讀取物件的記憶體地址,可以看到也是分配的16個位元組

OC物件的分類
OC物件主要分為三種
- instance物件(實體物件)
- class物件(類物件)
- meta-class物件(元類物件)
instance物件
instance物件就是通過類alloc出來的物件,每次呼叫alloc都會產生新的instance物件
// object1、object2是NSObject的instance物件(實體物件)
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
// 通過列印可以看出,它們是不同的兩個物件,分別占據著兩塊不同的記憶體
NSLog(@"instance - %p %p",
object1,
object2);
instance物件在記憶體中存盤的資訊
- isa指標
- 其他成員變數的具體值

class物件
每個類在記憶體中有且只有一個class物件
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
// 通過列印可以看出,上面幾個方法回傳的都是同一個類物件,記憶體地址都一樣
NSLog(@"class - %p %p %p %p %p %d",
objectClass1,
objectClass2,
objectClass3,
objectClass4,
objectClass5);
注意:class方法回傳的一直是類物件,所以哪怕這樣寫還是會回傳類物件
Class objectMetaClass2 = [[[NSObject class] class] class];
class物件在記憶體中存盤的資訊
- isa指標- superclass指標- 類的屬性資訊(@property)、類的物件方法資訊(instance method)- 類的協議資訊(protocol)、類的成員變數資訊(ivar)
- ....

meta-class物件
objectMetaClass是NSObject的meta-class物件(元類物件),每個類在記憶體中有且只有一個meta-class物件
Class objectMetaClass = object_getClass(objectClass5);
meta-class物件和class物件的記憶體結構是一樣的,但是用途不一樣,在記憶體中存盤的資訊主要包括
- isa指標- superclass指標- 類的類方法資訊(class method)
- ....

使用class_isMetaClass(Class _Nullable cls)來查看Class是否為meta-class的方法
NSLog(@"objectMetaClass - %p %d", objectMetaClass, class_isMetaClass(objectMetaClass));
isa和superclass
每個類的實體物件、類物件、元類物件都有一個isa指標
-
instance的isa指向class - 當呼叫物件方法時,通過instance的isa找到class,最后找到物件方法的實作進行呼叫
-
class的isa指向meta-class - 當呼叫類方法時,通過class的isa找到meta-class,最后找到類方法的實作進行呼叫
-
meta-class的isa指向基類的meta-class每個類的類物件、元類物件都有一個
superclass指標 -
class的superclass指標指向父類的class
- 如果沒有父類,superclass指標為nil
-
meta-class的superclass指向父類的meta-class
- 基類的meta-class的superclass指向基類的class
instance呼叫物件方法的軌跡
- isa找到class,方法不存在,就通過superclass找父類
class呼叫類方法的軌跡
- isa找meta-class,方法不存在,就通過superclass找父類

Class型別的底層結構
我們可以從原始碼objc-runtime-new.h檔案中找到Class型別的本質是結構體objc_class型別,里面包含了superclass指標、cache方法快取,以及獲取具體的類資訊的class_data_bits_t型別的屬性表
struct objc_class : objc_object {
// Class ISA;
// superclass指標
Class superclass;
// 方法快取
cache_t cache; // formerly cache pointer and vtable
// 用于獲取具體的類資訊
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
// rw意為readwrite,可讀可寫,t意為table,表格
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
}
繼承的父類objc_object里有一個isa指標
// 繼承的父類結構體里面有一個isa指標
struct objc_object {
private:
isa_t isa;
public:
Class ISA(bool authenticated = false);
Class rawISA();
Class getIsa();
uintptr_t isaBits() const;
....
};
分析class_data_bits_t這個型別里面的結構可以看出,bits & FAST_DATA_MASK就可以得到class_rw_t型別的表的記憶體
// class_data_bits_t結構體里的具體分析
struct class_data_bits_t {
friend objc_class;
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
}
分析class_rw_t這個型別里面的結構可以看出,里面有方法串列、屬性串列、協議串列,以及class_ro_t型別的屬性表
// class_rw_t結構體里的具體分析
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
// ro意為readonly,只讀
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_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
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_or_rw_ext)->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 *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
// 協議串列
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 *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
分析class_ro_t這個型別的結構可以看出,instanceSize意為實體物件所占用的記憶體空間,name存盤的是類名,ivars存盤的成員變數列表
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
// 實體物件占用記憶體大小空間
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
// 類名
explicit_atomic<const char *> name;
void *baseMethodList;
protocol_list_t * baseProtocols;
// 成員變數串列
const ivar_list_t * ivars;
}
總結:
上述分析可以簡單用一張圖來概述

isa指標
在arm64架構之前,isa就是一個普通的指標,存盤著Class、Meta-Class物件的記憶體地址
從arm64架構開始,對isa進行了優化,變成了一個isa_t型別的共用體(union)結構,共用體就是多種資料結構都共用同一塊存盤空間,里面包含了bits、cls、ISA_BITFIELD結構體以及其他的函式或變數,它們都是共用同一塊記憶體空間的
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // 現在的版本用一個宏來定義
};
}
在isa.h中查看ISA_BITFIELD這個結構體,里面的每一個值都是位域,不同架構下的掩碼和位域都是不一樣的,我們只以arm64架構的來分析
// 在isa.h中查看ISA_BITFIELD
// 每個變數后面標的數字就是位域
// 類似ISA_MASK這種宏的都叫掩碼
# if __arm64__
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# 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 unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
....
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
每一位位域對應的二進制位的排序都是從右向左的,下面是對應的每個位域的含義

上述代碼里類似ISA_MASK這樣的值都是掩碼,以掩碼ISA_MASK為例,轉成二進制發現對應是1的部分都是用來取值的

而且一共有33位的1,正好對應著shiftcls這個位域的位數,shiftcls又是存盤著類物件和元類物件的地址值,那么就能說明在arm64架構之后的isa里存盤著更多的資訊,需要&ISA_MASK進行一次位運算之后才能將類物件和元類物件的真實地址值取出來
位運算的運用實體
利用共用體和位運算來優化屬性的記憶體空間
創建Person.h檔案,然后手動實作setter和getter
@interface Person : NSObject
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
利用共用體的本質,在Person.m的類擴展中創建一個私有的共用體型別的變數
@interface Person()
{
union {
char bits;
struct {
char tall : 1;
char rich : 1;
char handsome : 1;
};
} _tallRichHandsome;
}
@end
該共用體一共只占有1個位元組,都是根據char bits來分配大小的,而sturct結構體是對這1個位元組大小的占用做說明的,里面每一個的1就是位域,指明占用了1個二進制位,雖然是char型別的,但都是根據位域后面給定的值來確定實際占用大小的,tall、rich、handsome三個變數都是占用著同一個記憶體區域,也就是值都會存盤在一個位元組里,這就是共用體的本質,這么做主要是為了做優化,節省記憶體空間,而且寫不寫這個結構體都是根據char bits來確定了分配空間大小的,沒有影響的
由于上述結構體里的三個變數占用一個位元組大小就足夠了,那么我們對應每一個變數用一個二進制位來存取值,我們先分別設定三個掩碼對應三個值
// 0x0000 0001
#define TallMask (1<<0)
// 0x0000 0010
#define RichMask (1<<1)
// 0x0000 0100
#define HandsomeMask (1<<2)
setter的實作如下,如果引數為YES,那么將掩碼進行按位或運算;如果引數為NO,那么先將掩碼取反,然后再進行按位與運算
@implementation Person
- (void)setTall:(BOOL)tall
{
if (tall) {
_tallRichHandsome.bits |= TallMask;
} else {
_tallRichHandsome.bits &= ~TallMask;
}
}
- (void)setRich:(BOOL)rich
{
if (rich) {
_tallRichHandsome.bits |= RichMask;
} else {
_tallRichHandsome.bits &= ~RichMask;
}
}
- (void)setHandsome:(BOOL)handsome
{
if (handsome) {
_tallRichHandsome.bits |= HandsomeMask;
} else {
_tallRichHandsome.bits &= ~HandsomeMask;
}
}
@end
getter的實作如下,先將掩碼進行按位與運算,然后再取反兩次;因為回傳值是BOOL型別,那么不是0就是1,所以按位與運算后的值只要不是0的都是有值的,那么取反兩次肯定就得到的不是0就是1了
- (BOOL)isTall
{
return !!(_tallRichHandsome.bits & TallMask);
}
- (BOOL)isRich
{
return !!(_tallRichHandsome.bits & RichMask);
}
- (BOOL)isHandsome
{
return !!(_tallRichHandsome.bits & HandsomeMask);
}
如此一來,我們就做到了優化了屬性的記憶體空間,而且也實作了setter和getter
利用位運算進行位移列舉的實作
創建一個位移列舉,每一個值都對應一個二進制位
typedef enum {
OptionsNone = 0, // 0b0000
OptionsOne = 1<<0, // 0b0001
OptionsTwo = 1<<1, // 0b0010
OptionsThree = 1<<2, // 0b0100
OptionsFour = 1<<3 // 0b1000
} Options;
和對應的列舉值進行按位與運算,就能得到是否存在該列舉值
@implementation ViewController
- (void)setOptions:(Options)options
{
if (options & OptionsOne) {
NSLog(@"包含了OptionsOne");
}
if (options & OptionsTwo) {
NSLog(@"包含了OptionsTwo");
}
if (options & OptionsThree) {
NSLog(@"包含了OptionsThree");
}
if (options & OptionsFour) {
NSLog(@"包含了OptionsFour");
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setOptions: OptionsOne | OptionsFour];
}
@end
面試題
1.一個NSObject物件占用多少記憶體?
系統分配了16個位元組給NSObject物件(通過malloc_size函式獲得)
但NSObject物件內部只使用了8個位元組的空間(64bit環境下,可以通過class_getInstanceSize函式獲得)
2.看下面代碼,分別描述Person和Student對應的記憶體占用
@interface Person : NSObject
{
int _height;
}
@end
@interface Student : Person
{
int _weight;
}
@end
Person *p = [[Person alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 16
malloc_size((__bridge const void *)(p))); // 16
Student *s = [[Student alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Student class]), // 16
malloc_size((__bridge const void *)(s))); // 16
默認在64bit處理器下,由于Person繼承自NSObject,所以根據記憶體對齊,系統給NSObject物件分配了16個位元組存放isa指標,Person的成員變數height由于是Int型別,占用4個位元組,因為isa指標實際只占用了8個位元組,還有多余的8個位元組空間,所以無需再多分配記憶體,那么Person的實際占用和系統分配都是16個位元組(記憶體對齊一般以成員變數占比最大的倍數來增加:isa指標占用8個位元組,占用最大,所以是8的倍數)
Student繼承自Person,isa指標和成員變數height實際占用了12個位元組,還有多余的4個位元組,而成員變數weight正好又占用4個位元組,那么也不用再分配更多的記憶體空間,Stuent物件的實際占用和系統分配也都是16個位元組
3.看下面代碼,描述Person的記憶體占用
@interface Person : NSObject
{
int _age;
int _height;
int _no;
}
@end
Person *p = [[Person alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 24
malloc_size((__bridge const void *)(p))); // 32
默認在64bit處理器下,由于Person繼承自NSObject,里面的isa指標實際占用了8個位元組,而Person里面有三個Int型別的成員變數,實際占用是12個位元組,由于結構體的記憶體對齊原則,系統要分配24個位元組(也就是3倍的isa指標的8個位元組)才能容納所有的成員變數,所以Person物件的實際占用為24個位元組,
但系統本身都會以16的倍數來進行記憶體分配,所以要分配大于實際占用位元組的兩倍才可以,所以Person物件的系統分配為分配32個位元組
4.看下面代碼,簡述Student的物件方法呼叫軌跡,然后分別注釋掉 + (void)test 方法和 - (void)test 方法后會怎樣呼叫
@interface NSObject (Test)
+ (void)test;
- (void)test;
@end
@implementation NSObject (Test)
+ (void)test
{
NSLog(@"+[NSObject test] - %p", self);
}
- (void)test
{
NSLog(@"-[NSObject test] - %p", self);
}
@interface Person : NSObject
+ (void)test;
- (void)test;
@end
@interface Student : Person
+ (void)test;
- (void)test;
@end
Student *s = [[Student alloc] init];
[s test];
[Student test];
1.[s test] 這個方法呼叫,首先Student的實體物件會根據isa指標去Student的類物件里面查找- (void)test方法,如果找到了則呼叫該方法,如果沒找到,那么就根據superclass指標去父類Person的類物件里查找,如果找到了則呼叫Person的- (void)test方法,如果沒找到,那么就根據superclass指標去基類NSObject的類物件里查找,如果找到了則呼叫NSObject的- (void)test方法,
如果注釋掉了NSObject的- (void)test方法,那么Student實體物件在基類NSObject的類物件里也找不到該方法,由于NSObject類物件的superclass指標指向nil,那么就會crash
2.[Student test] 這個方法呼叫,首先Student的類物件會根據isa指標去Student的元類物件里查找+ (void)test方法,如果找到了則呼叫該方法,如果沒找到,那么就根據superclass指標去父類Person的元類物件里查找,如果找到了則呼叫Person的+ (void)test方法,如果沒找到,那么就根據superclass指標去基類NSObject的元類物件里查找,如果找到了則呼叫NSObject的+ (void)test方法,
如果注釋掉了NSObject的+ (void)test方法,那么Student的類物件在基類NSObject的元類物件里也找不到該方法,由于NSObject元類物件的superclass指標指向NSObject的類物件,所以就會呼叫NSObject類物件的- (void)test方法,
如果NSObject的兩個方法都注釋掉了,那么由于上一步的邏輯會去NSObject類物件里呼叫- (void)test方法,該方法也找不到,那么NSObject類物件的superclass指標是指向nil的,最后還是會crash
iOS的訊息機制本質就是訊息呼叫,所以不會真的區分類方法和物件方法,都是根據方法名進行查找
5.isMemberOfClass、isKindOfClass、isSubclassOfClass的區別,并說下原理
我們先通過一段代碼列印可以得知
Person *person = [[Person alloc] init]; // Person物件
NSObject *obj = [[NSObject alloc] init]; // NSObject物件
Class person_class = [person class]; // Person類物件
Class obj_class = [obj class]; // NSObject類物件
Class person_meta_class = object_getClass(person_class); // Person元類物件
Class obj_meta_class = object_getClass(obj_class); // NSObject元類物件
Class person_meta_meta_class = object_getClass(person_meta_class); // NSObject元類物件
Class obj_meta_meta_class = object_getClass(obj_meta_class); // NSObject元類物件
// Person物件, NSObject物件, Person類物件,NSObject類物件
NSLog(@"%@, %@, %@, %@", person, obj, person_class, obj_class);
// Person元類物件, NSObject元類物件, NSObject元類物件,NSObject元類物件
NSLog(@"%@, %@, %@, %@", person_meta_class, obj_meta_class, person_meta_meta_class, obj_meta_meta_class);
isMemberOfClass
我們在objc4原始碼的NSObject.mm里可以看到,isMemberOfClass的類方法會拿到isa指標所指的物件和傳進來的型別做比較;物件方法會拿當前類物件來做比較
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
我們可以通過一段代碼列印來分析比較
// Person類物件, Person類物件
NSLog(@"%d", [person isMemberOfClass:person_class]); // 1
// Person類物件, NSObject類物件
NSLog(@"%d", [person isMemberOfClass:obj_class]); // 0
// NSObject類物件, NSObject類物件
NSLog(@"%d", [obj isMemberOfClass:obj_class]); // 1
// Person元類物件, Person類物件
NSLog(@"%d", [person_class isMemberOfClass:person_class]); // 0
// Person元類物件, NSObject類物件
NSLog(@"%d", [person_class isMemberOfClass:obj_class]); // 0
// NSObject元類物件, NSObject類物件
NSLog(@"%d", [obj_class isMemberOfClass:obj_class]); // 0
// Person元類物件, Person元類物件
NSLog(@"%d", [person_class isMemberOfClass:person_meta_class]); // 1
// Person元類物件, NSObject元類物件
NSLog(@"%d", [person_class isMemberOfClass:obj_meta_class]); // 0
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [obj_class isMemberOfClass:obj_meta_class]); // 1
// 所有型別的元類物件的isa指標都指向NSObject的元類物件,包括NSObject的元類物件自己
// NSObject元類物件, Person元類物件
NSLog(@"%d", [person_meta_class isMemberOfClass:person_meta_class]); // 0
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [person_meta_class isMemberOfClass:obj_meta_class]); // 1
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [obj_meta_class isMemberOfClass:obj_meta_class]); // 1
isKindOfClass
isKindOfClass的類方法會拿到isa指標所指向的物件以及該物件的superclass指標所指向的物件和傳進來的型別做比較;物件方法會拿當前類物件以及該物件的superclass指標所指向的物件來做比較
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
我們可以通過一段代碼列印來分析比較
// Person類物件, Person類物件
NSLog(@"%d", [person isKindOfClass:person_class]); // 1
// Person類物件, NSObject類物件
NSLog(@"%d", [person isKindOfClass:obj_class]); // 1
// NSObject類物件, NSObject類物件
NSLog(@"%d", [obj isKindOfClass:obj_class]); // 1
// Person元類物件, Person類物件
NSLog(@"%d", [person_class isKindOfClass:person_class]); // 0
// Person元類物件的superclass指向NSObject元類物件,而NSObject元類物件的superclass指向的就是NSObject類物件
// Person元類物件, NSObject類物件
NSLog(@"%d", [person_class isKindOfClass:obj_class]); // 1
// NSObject元類物件, NSObject類物件
NSLog(@"%d", [obj_class isKindOfClass:obj_class]); // 1
// Person元類物件, Person元類物件
NSLog(@"%d", [person_class isKindOfClass:person_meta_class]); // 1
// Person元類物件, NSObject元類物件
NSLog(@"%d", [person_class isKindOfClass:obj_meta_class]); // 1
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [obj_class isKindOfClass:obj_meta_class]); // 1
// NSObject元類物件, Person元類物件
NSLog(@"%d", [person_meta_class isKindOfClass:person_meta_class]); // 0
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [person_meta_class isKindOfClass:obj_meta_class]); // 1
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [obj_meta_class isKindOfClass:obj_meta_class]); // 1
isSubclassOfClass
isSubclassOfClass的類方法會拿到當前類物件以及superclass指標所指向的對象和傳進來的型別做比較;該方法沒有物件方法
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
我們可以通過一段代碼列印來分析比較
// Person類物件, Person類物件
NSLog(@"%d", [person_class isSubclassOfClass:person_class]); // 1
// Person類物件, NSObject類物件
NSLog(@"%d", [person_class isSubclassOfClass:obj_class]); // 1
// NSObject類物件, NSObject類物件
NSLog(@"%d", [obj_class isSubclassOfClass:obj_class]); // 1
// Person類物件, MJPerson元類物件
NSLog(@"%d", [person_class isSubclassOfClass:person_meta_class]); // 0
// Person類物件, NSObject元類物件
NSLog(@"%d", [person_class isSubclassOfClass:obj_meta_class]); // 0
// NSObject類物件, NSObject元類物件
NSLog(@"%d", [obj_class isSubclassOfClass:obj_meta_class]); // 0
// Person元類物件, Person元類物件
NSLog(@"%d", [person_meta_class isSubclassOfClass:person_meta_class]); // 1
// Person元類物件, NSObject元類物件
NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_meta_class]); // 1
// NSObject元類物件, NSObject元類物件
NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_meta_class]); // 1
// Person元類物件, Person類物件
NSLog(@"%d", [person_meta_class isSubclassOfClass:person_class]); // 0
// Person元類物件, NSObject類物件
NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_class]); // 1
// NSObject元類物件, NSObject類物件
NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_class]); // 1
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/266632.html
標籤:其他
