我们平时编写的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类对象, Person元类对象
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