一、WWDC2020 - 类优化
WWDC2020的Advancements in the Objective-C runtime重点通过讲解了Objective-C底层优化过程,可以了解到class类的构成与运行时的DirtyMemory、CleanMemory的情况。本质上就是iOS没有swap方法管理DirtyMemory,尽量保存不改变的类数据到CleanMemory。
CleanMemory与DirtyMemory
dirty memory要比clean memory昂贵的多,只要进行运行它就必须一直存在,通过分离出那些不会被改变的数据,可以把大部分的类数据存储为clean memory,这是苹果追求的。
Clean Memory
clean memory加载后不会发生改变的内存class_ro_t就属于clean memory,因为它是只读的,不会对其内存进行修改clean memory是可以进行移除的,从而节省更多的内存空间,因为如果你有需要clean memory,系统可以从磁盘中重新加载
Dirty Memory
dirty memory是指在进程运行时会发生改变的内存- 类结构一经使用就会变成
dirty memory,因为运行时会向它写入新的数据。例如创建一个新的方法缓存并从类中指向它,初始化类相关的子类和父类 dirty memory是类数据被分成两部分的主要原因
类class的运行时的Memory变化与优化
- 最开始在
app的二进制码中存在的是dirty memory是MYClass用户自定的class,与生成该类的clean memory的class_ro_t。 - 当这个类
MYClass首次被使用时,运行时会为它分配额外的dirty memory的class_rw_t用于读取-编写数据(重点通过这个数据结果观察运行时状态) - 但是在这次WWDC的优化过程把这
class_rw_t经常变动的部分单独分离出来到class_rw_ext_t,只留下类的指向。
- 老结构图:
- 优化完的结构图:
class_rw_t的数据结构:
-
First Subclass、Next Subling Class:包含了运行时才会生成的信息 -
First Subclass、Next Subling Class,所有的类都会变成一个树状结构,就是通过First Subclass和Next Subling Class指针实现的,它允许运行时遍历当前使用的所有类
class_rw_ext_t的数据结构:
-
Methods、Properties、protocols:包含这3个是因为它们可以在运行时进行修改,当category被加载时,它可以向类中添加新的方法,也可以通过runtime API添加它们 -
Demangled Name:这个是只有Swift才会使用的字段,因为整个数据结构OC与Swift是共享的,但是Swift类本身并不需要这个字段,是为了有人要访问Swfit的OC名称的时候使用的,利用率比较低。
- 关系图:
classDiagram
MYClass --|> class_rw_t:DirtyMemory
class_rw_t --|> class_rw_ext_t:DirtyMemory
class_rw_ext_t --|> class_ro_t:CleanMemory
class MYClass{
Metaclass
SuperClass
Flags
Method cache
}
class class_rw_t{
Flags
First Subclass
Next Sibling Class
}
class class_rw_ext_t{
Methods
Properties
Protocols
Demangled Name
}
class class_ro_t{
Flags
Size
Name
Methods
Protocols
Ivars
Properties
}
二、类的结构编码
SEL和IMP关系
-
SEL:方法编号,相当于书本的目录名称 -
IMP:函数指针地址,相当于书的页码
查找方法文档
类型编码图的获取途径:打开xcode--> command+shift+0--> 搜索ivar_getTypeEncoding--> 点击Type Encodings
官方编码表
-
编码表: | Code | Meaning | | --- | --- | | c |A char| | i | An int | | s |A short| | l |A long, is treated as a 32-bit quantity on 64-bit programs.| | q | A long long | | c |An unsigned char| | I |An unsigned int| | S | An unsigned short | | L |An unsigned long | | Q | An unsigned long long | | f |A float| | d |A double| | B | A C++ bool or a C99 _Bool | | v |A void | | * | A character string (char *) | | @ |An object (whether statically typed or typed id)| | # |A class object (Class)| | : | A method selector (SEL) | | [array type] |An array| | {name=type...} |A structure| | (name=type...) |A union| | bnum |A bit field of num bits| | ^type |A pointer to type| | ? |An unknown type (among other things, this code is used for function pointers)|
-
验证代码:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#pragma mark - 各种类型编码
void lgTypes(void){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
}Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// objc_object VS objc_class
// person VS NSObject
// id objc_object *
// class objc_class *
lgTypes();
}
return 0;
}
- llvm打印:
2021-07-01 01:03:52.622628+0800 001-类的属性与变量[92033:3820537] char --> c
2021-07-01 01:03:52.623543+0800 001-类的属性与变量[92033:3820537] int --> i
2021-07-01 01:03:52.623621+0800 001-类的属性与变量[92033:3820537] short --> s
2021-07-01 01:03:52.623666+0800 001-类的属性与变量[92033:3820537] long --> q
2021-07-01 01:03:52.623698+0800 001-类的属性与变量[92033:3820537] long long --> q
2021-07-01 01:03:52.623817+0800 001-类的属性与变量[92033:3820537] unsigned char --> C
2021-07-01 01:03:52.623884+0800 001-类的属性与变量[92033:3820537] unsigned int --> I
2021-07-01 01:03:52.623939+0800 001-类的属性与变量[92033:3820537] unsigned short --> S
2021-07-01 01:03:52.623995+0800 001-类的属性与变量[92033:3820537] unsigned long --> Q
2021-07-01 01:03:52.624076+0800 001-类的属性与变量[92033:3820537] float --> f
2021-07-01 01:03:52.624146+0800 001-类的属性与变量[92033:3820537] bool --> B
2021-07-01 01:03:52.624191+0800 001-类的属性与变量[92033:3820537] void --> v
2021-07-01 01:03:52.624248+0800 001-类的属性与变量[92033:3820537] char * --> *
2021-07-01 01:03:52.631074+0800 001-类的属性与变量[92033:3820537] id --> @
2021-07-01 01:03:52.631115+0800 001-类的属性与变量[92033:3820537] Class --> #
2021-07-01 01:03:52.631153+0800 001-类的属性与变量[92033:3820537] SEL --> :
2021-07-01 01:03:52.631189+0800 001-类的属性与变量[92033:3820537] int[] --> [3i]
2021-07-01 01:03:52.631226+0800 001-类的属性与变量[92033:3820537] struct --> {person=*i}
2021-07-01 01:03:52.631262+0800 001-类的属性与变量[92033:3820537] union --> (union_type=*i)
2021-07-01 01:03:52.631301+0800 001-类的属性与变量[92033:3820537] int[] --> ^i
方法编码符号
定义一个LGPerson类,自定义属性和成员变量,在终端使用xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp生成.cpp文件。
- 源代码:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface LGPerson : NSObject
{
// STRING int double float char bool
NSString *hobby; // 字符串
int a;
NSObject *objc; // 结构体
}
@property (nonatomic, copy) NSString *nickName;
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
person.name = @"KC"; // setName -> objc_setPropety
NSLog(@"Hello, World!");
}
return 0;
}
- 生成
main.cpp的部分源码:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[24];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
24,
{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_},
{(struct objc_selector *)"nnickName", "@16@0:8", (void *)_I_LGPerson_nnickName},
{(struct objc_selector *)"setNnickName:", "v24@0:8@16", (void *)_I_LGPerson_setNnickName_},
{(struct objc_selector *)"anickName", "@16@0:8", (void *)_I_LGPerson_anickName},
{(struct objc_selector *)"setAnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAnickName_},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
{(struct objc_selector *)"aname", "@16@0:8", (void *)_I_LGPerson_aname},
{(struct objc_selector *)"setAname:", "v24@0:8@16", (void *)_I_LGPerson_setAname_},
{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_},
{(struct objc_selector *)"nnickName", "@16@0:8", (void *)_I_LGPerson_nnickName},
{(struct objc_selector *)"setNnickName:", "v24@0:8@16", (void *)_I_LGPerson_setNnickName_},
{(struct objc_selector *)"anickName", "@16@0:8", (void *)_I_LGPerson_anickName},
{(struct objc_selector *)"setAnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAnickName_},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
{(struct objc_selector *)"aname", "@16@0:8", (void *)_I_LGPerson_aname},
{(struct objc_selector *)"setAname:", "v24@0:8@16", (void *)_I_LGPerson_setAname_}}
};
- 找到
nickName:"@16@0:8"做讲解:
@16@0:8
| Code | Meaning |
|---|---|
@ | id |
16 | 占用内存 |
@ | id |
0 | 从0位置开始 |
: | SEL |
8 | 从8号位置开始 |
三、成员变量、实例变量与属性
- 实例变量+基本数据类型变量=成员变量
ivar成员变量(分类没有)+setter方法+getter方法=propetry属性
-
成员变量:
int、double、float、char、bool等基础类型变量与实例变量,在OC底层用ivar表示 -
实例变量:对象等引用类型变量
-
属性:是
@interface有通过@property定义的变量,编译器会自动生成setter、getter、ivar(分类中没有生成成员变量,要用关联对象)
四、属性的setter与getter方法实现方式
关系图:
graph TD
setter --> objc_setProperty
setter --> 内存偏移赋值
getter --> objc_getProperty
getter --> 读取偏移内存
验证属性的方法
-
objc_setProperty是一个中间层代码。为了每一个属性在使用setter方法的时候,不必为其在底层创建单独的实现方法;当检测到属性对象使用copy修饰的时候,会将此对象的setter方法重定向到objc_setProperty(),然后只要在底层实现setProperty方法就可以将属性对象的setter方法实现了。 -
objc_getProperty也是个中间层代码。只有属性既是atomic,也是copy时,才会将对象属性的getter方法重定向到objc_getProperty。
定义一个LGPerson类,添加各种修饰词(weak、strong、weak、nonatomic、atomic)的自定义属性和成员变量,终端输出xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp生成main.cpp文件。
- objc代码:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface LGPerson : NSObject
{
// STRING int double float char bool
NSString *hobby; // 字符串
int a;
NSObject *objc; // 结构体
}
@property (nonatomic, copy) NSString *nickName;
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
@property (nonatomic,assign) NSInteger height;
@property (atomic,assign) NSInteger weight;
@end
@implementation LGPerson
@end
@interface LGTeacher : NSObject
@end
@implementation LGTeacher
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// objc_object VS objc_class
// person VS NSObject
// id objc_object *
// class objc_class *
LGPerson *person = [LGPerson alloc];
person.name = @"KC"; // setName -> objc_setPropety
LGTeacher *teacher = [LGTeacher alloc];
// Class tClass = object_getClass(teacher);
Class pClass = object_getClass(person);
NSLog(@"Hello, World!");
}
return 0;
}
- 生成c++部分代码:
static NSString * _I_LGPerson_nickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
static NSString * _I_LGPerson_acnickName(LGPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), 1); }
static void _I_LGPerson_setAcnickName_(LGPerson * self, SEL _cmd, NSString *acnickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), (id)acnickName, 1, 1); }
static NSString * _I_LGPerson_nnickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)); }
static void _I_LGPerson_setNnickName_(LGPerson * self, SEL _cmd, NSString *nnickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)) = nnickName; }
static NSString * _I_LGPerson_anickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)); }
static void _I_LGPerson_setAnickName_(LGPerson * self, SEL _cmd, NSString *anickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)) = anickName; }
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)) = name; }
static NSString * _I_LGPerson_aname(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)); }
static void _I_LGPerson_setAname_(LGPerson * self, SEL _cmd, NSString *aname) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)) = aname; }
static NSInteger _I_LGPerson_height(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_height)); }
static void _I_LGPerson_setHeight_(LGPerson * self, SEL _cmd, NSInteger height) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_height)) = height; }
static NSInteger _I_LGPerson_weight(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_weight)); }
static void _I_LGPerson_setWeight_(LGPerson * self, SEL _cmd, NSInteger weight) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_weight)) = weight; }
- c++图片:
- 结论:
-
属性的
setter方法的实现方式有两种:objc_setProperty和内存偏移 -
属性的
getter方法也有两种实现方式:objc_getProperty和内存偏移 -
属性用
copy、atomic修饰的时候,会将此变量的setter、getter方法重定向到objc_setProperty()、objc_getProperty() -
属性只用
copy就会使用objc_setProperty(),而只有同时使用copy、atomic才会使用objc_getProperty() -
如果声明的对象不设置任何除原子性之外的属性,那么默认属性是
strong,不会触发objc_setProperty、objc_getProperty
objc_setProperty与objc_getProperty
objc_setProperty与objc_getProperty是objc4.8源码里属性的成员变量的setter与getter方法,本质上还是使用内存偏移找到成员变量进行读写;但是过程中通过atomic判断是否在读写过程加自旋锁(spinlock_t),或者copy涉及新值的深浅拷贝问题。
- objc4.8各种类型
objc_setProperty源代码:
// 属性设置方法
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);
}
// 原子属性设置方法
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
// 非原子属性设置方法
void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}
// 原子复制属性设置方法
void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}
// 非原子复制属性设置方法
void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}
- objc4.8
objc_getProperty、reallySetProperty源代码:
// 属性获取方法
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
// 属性设置方法
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);
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);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
- 结论:
- 无论使用什么修饰词的属性的
setter方法objc_setProperty、objc_setProperty_atomic等等,最终都是调用了reallySetProperty - 属性的
setter方法只有一个objc_getProperty方法
LLVM验证对象属性为copy时,setter与getter
- llvm源码验证流程:
objc_setProperty->getSetPropertyFn-GetPropertySetFunction->PropertyImplStrategy->IsCopy(判断)objc_getProperty->getGetPropertyFn-GetPropertyGetFunction->PropertyImplStrategy->IsCopy(判断)
objc_setProperty与objc_getProperty图:
-
getSetPropertyFn与getGetPropertyFn图: -
GetPropertySetFunction与GetPropertyGetFunction图:
-
GetPropertySetFunction被调用图: -
GetPropertyGetFunction被调用图: -
PropertyImplStrategy图:
五、补充其他内容点
isKindOfClass和isMemberOfClass
- 查找官方文档可以知道
isKindOfClass是方法调用者是传入的类的实例对象,或者调用者是传入类的继承者链中的类的实例对象,则返回YES。 isMemberOfClass是方法调用者必须是传入的类的实例对象才返回YES。
-
通过一个实例来探究下
isKindOfClass和isMemberOfClass -
代码:
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
void lgKindofDemo(void){
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
lgKindofDemo();
}
return 0;
}
- llvm打印:
2021-07-03 00:11:28.157280+0800 KCObjcBuild[11864:4389325] re1 :1
re2 :0
re3 :0
re4 :0
2021-07-03 00:11:28.157470+0800 KCObjcBuild[11864:4389325] re5 :1
re6 :1
re7 :1
re8 :1
- 汇编调试方法图:
- 结论:
- 在汇编代码中发现其实编译器做了优化,
hook了isKindOfClass方法为调用了objc_opt_isKindOfClass;hook了isMemberOfClass方法为调用了objc_opt_class。 - 下一步分析
objc_opt_isKindOfClass、isKindOfClass、objc_opt_class和isMemberOfClass这四个方法。
objc_opt_isKindOfClass底层原理
在objc4.8源码中的objc-internal.h中可以知道支持的版本masOS大于10.15,iOS大于13.0
OBJC_EXPORT BOOL
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
- 具体实现:
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__ //现在基本上都用OBJC2版本
//slowpath(!obj) obj为空的是小概率事件基本不会发生
if (slowpath(!obj)) return NO;
// 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
Class cls = obj->getIsa();
//fastpath(!cls->hasCustomCore()) (类或者父类中大概率没有默认的isKindOfClass方法)
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif //OBJC版本直接走消息转发
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
- 结论:
obj->getIsa()获取类或者元类:obj是对象就获取类,obj是类就获取元类 然后就接着for循环,for里面的代码和isKindOfClass逻辑一样就不进行分析了,可以直接看isKindOfClass的底层原理。
isKindOfClass底层原理
- 具体实现:
+ (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;
}
- 结论:
-
+ isKindOfClass流程。类的元类vscls(需要比较的类),不同继续比较 。元类的父类vscls,不同继续比较直到找到根元类。根元类vscls,不同继续比较。根类(NSObject)vscls,如果还不相同则根类(NSObject)的父类为nil,跳出循环返回NO -
- isKindOfClass流程。获取当前对象所属类,类vscls,不同继续比较 。类的父类vscls,不同继续比较直到找到根类(NSObject)。根类(NSObject)vscls,如果还不相同则根类(NSObject)的父类为nil,跳出循环返回NO
objc_opt_class底层原理
在objc4.8源码中的objc-internal.h中可以知道支持的版本masOS大于10.15,iOS大于13.0
OBJC_EXPORT Class _Nullable
objc_opt_class(id _Nullable obj)
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
- 具体实现:
// Calls [obj class]
Class
objc_opt_class(id obj)
{
#if __OBJC2__
if (slowpath(!obj)) return nil;
// 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
Class cls = obj->getIsa();
//(类或者父类中大概率没有默认的class方法)
if (fastpath(!cls->hasCustomCore())) {
//cls是类 返回cls,如果cls是元类,obj是类,返回obj还是类
return cls->isMetaClass() ? obj : cls;
}
#endif
return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
}
- 结论:
objc_opt_class的实现就是获取类,如果参数是对象则返回类,如果是类就返回类
isMemberOfClass底层原理
- 具体实现:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- 结论:
-
+ isMemberOfClass流程。类的元类vscls(需要比较的类),相同就返回YES,否则返回NO -
- isMemberOfClass流程。类vscls(需要比较的类),相同就返回YES,否则返回NO