WWDC 2020 - 运行时的优化
主要对运行时做了三个地方的优化:
- 数据结构改变。OC在运行时会使用他们进行追踪类。
- OC方法列表的变化
- tagged pointer格式的变化 官方地址
数据结构改变
Clean memory
和Dirty memory
的区别
Clean memory
- 加载后不会改变的内存(如:class_ro_t就在这里,因为他是只读)
- 这里可以删除,从而节约更多的空间,需要时在磁盘中重新创建。 Dirty memory
- 在运行时就发生改变的内存(如:
类结构
一经过使用就会变成dirty memory,因为他在运行时会写入数据,比如创建一个新的方法缓存,并从类中指向他) 所以,在运行时内存消耗就会很多,优化更多的运行时内存就变得很重要。最新版本,就对运行时的数据结构进行了改造,把动态
添加的方法
,协议
,属性
等添加到class_rw_ext_t
中.
当类第一次从磁盘加载时的结构
myclass
在Dirty Memory
内存区,class_ro_t
在Clean Memory
内存区
当类第一次使用时的结构
第一次使用时会为他分配额外的存储容量,class_rw_t
用于读写数据,在这里存储了只有在运行时才会生成的数据(如:所有的类都会链接成一个树状结构,是通过first subclass
和next sibling class
来实现,可以遍历所有当前使用的类)。同时存在运行时更改的方法
,属性
,协议
(当分类加载时,会向类中添加新的方法)。这样会造成内存太大,为了减少内存,又把不常用的方法属性
和协议
放在了class_rw_ext_t
中,提高内存使用效率。
将需要动态更新的部分提取出来,存⼊class_rw_ext_t
当类使用到了扩展部分的方法
class_rw_t
优化,其实就是对class_rw_t
不常用的部分进行了剥离。如果需要用到这部分就从扩展记录中分配一个,滑到类中供其使用。
方法列表改变
每个类都有一个方法列表,每个方法都有三个参数:名称
、参数和返回值
和指向方法实现的指针
。这些参数都是以指针进行存储,当使用时去查询该指针指向的区域即可。以前使用了8字节(64位)存储这些指针,但是因为指针和指向的内存基本上都在一个二进制下,其实并不需要64位,所以把64位
偏移量查询方法实现改成了32位偏移查询。这样可以节约一半的内存。
Tagged pointer格式的改变
是对64位的存储信息的优化,增加读取的效率。因为一般情况下,64位的信息是不会存储满的,在高位和低位都是0,那么在这些位置可以增加一些其他信息,比如存储的类型 是否是指针等信息,方便读取使用。增加运行时的效率。
类的成员变量和属性
- 成员变量是指类声明大括号中的变量。包含基本类型变量(
int
,NSString
等类型),实例变量(对象)。 - 成员变量用于类内部,无需与外界接触的变量
- 成员变量:在底层没有其他操作只是变量的声明
- 属性:系统会自动在底层添加了_属性名变量,同时生成setter和getter方法
需要研究的代码
{
NSString *hobby;
}
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
可以使用clang
把类编译成.cpp文件。参考这里
编译后结果如下
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *hobby;
NSString *_nickName;
NSString *_acnickName;
};
...
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; }
...
- 成员变量仅仅只是变量声明
- 属性不仅有变量声明,还有
get
、set
方法 - 属性不同的修饰符,都有对应的实现方法。
copy
修饰的属性采用了objc_getProperty
,objc_setProperty
方法。- 其他修饰符修饰的属性使用的是
地址+偏移量
进行赋值
和取值
查看objc
的源码,分析set
、get
实现原理。获取oc源码
- 全局搜索
objc_setProperty
,找到如下几个类型的方法
- 搜索
reallySetProperty
,可以看到其实现方法
- 搜索
objc_getProperty
也可以使用LLVM
源码,进行分析底层的实现(仅参考)
根据clang编译后的文件中的方法objc_setProperty
,objc_getProperty
,去下载好的LLVM文件中 LLVM-project反过来推导其方法过程。
- 搜索
objc_setProperty
- 搜索
getSetPropertyFn
,图片省略 - 搜索
GetPropertySetFunction
,图片省略 - 搜索
PropertyImplStrategy
,图片省略
补充
isKindOfClass
与isMemberOfClass
的区别
isKindOfClass
底层实现
// 元类 -> 根元类 -> NSObject -> nil
//类的元类与cls比较
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
//对象的类与cls比较
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
isKindOfClass
底层实现
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls; //类的元类和cls比较
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;//类 = 类
}
实例
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);
输出
re1 :1
re2 :0
re3 :0
re4 :0
re5 :1
re6 :1
re7 :1
re8 :1
对执行结果进行分析
- re1 : 1 ,是
NSObject
的元类与NSObject
的对比,使用+isKindOfClass
NSObject的元类是根元类,与NSObject比较,不相等
获取根元类的父类即NSObject,与NSObject相等
- re2 : 0 ,是
NSObject
的元类与NSObject
的对比,使用+isMemberOfClass
NSObject的元类是根元类,与NSObject比较,不相等
- re3 : 0 ,是
LGPerson
的元类与LGPerson
的对比,使用+isKindOfClass
LGPerson的元类,与LGPerson比较,不相等
元类的父类是根元类,与LGPerson比较,不相等
根元类的父类是NSObject,与LGPerson比较,不相等
- re4 : 0 ,是
LGPerson
的元类与NSObject
的对比,使用+isMemberOfClass
LGPerson的元类,与LGPerson比较,不相等
- re5 : 1 ,是
NSObject
与NSObject
的对比,使用-isKindOfClass
NSObject与NSObject比较,相等
- re6 : 1 ,是
NSObject
与NSObject
的对比,使用-isMemberOfClass
NSObject与NSObject比较,相等
- re7 : 1 ,是
LGPerson
与LGPerson
的对比,使用-isKindOfClass
LGPerson与LGPerson比较,相等
- re8 : 1 ,是
LGPerson
与NSObject
的对比,使用-isMemberOfClass
LGPerson与LGPerson比较,相等