Runtime系列文章
Runtime笔记(一)—— isa的深入体会(苹果对isa的优化)
Runtime笔记(二)—— Class结构的深入分析
Runtime笔记(三)—— OC Class的方法缓存cache_t
Runtime笔记(四)—— 刨根问底消息机制
Runtime笔记(五)—— super的本质
[Runtime笔记(六)—— Runtime的应用…待续]-()
[Runtime笔记(七)—— Runtime的API…待续]-()
Runtime笔记(八)—— 记一道变态的runtime面试题
☕️☕️本文篇幅比较长,创作的目的并不是为了在简书上刷赞和阅读量,而是为了自己日后温习知识所用。如果有幸被你发现这篇文章,并且引起了你的阅读兴趣,请休息充分,静下心来,精力充足地开始阅读,希望这篇文章能对你有所帮助。如发现任何有误之处,肯请留言纠正,谢谢。☕️☕️
如何理解Objective-C的动态特性?
很多静态编程语言,编写完代码后,经过编译连接生成可执行文件,最后就可以在电脑上运行起来。
以C语言为例
void test() {
printf("Hello World");
}
int main() {
test();
}
以上代码经过编译之后,main
函数里面就一定会调用test()
,而test()
的实现也一定会是和代码中写的一样,这些在编译完成那一刻就决定了,运行过程中不会发生改变的。C可以说就是典型的静态语言。
与之相比,Objective-C就可以在运行阶段修改之前编译阶段确定好的一些函数和方法。
************************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLPerson *person = [[CLPerson alloc] init];
[person test];
}
return 0;
}
***********************CLPerson.h************************
#import <Foundation/Foundation.h>
@interface CLPerson : NSObject
- (void)test;
@end
***********************CLPerson.m************************
#import "CLPerson.h"
@implementation CLPerson
- (void)test {
NSLog(@"%s", __func__);
}
- (void)abc {
}
@end
如上面所示代码,[person test];
这句代码,在运行阶段,可以调用CLPerson
的test
方法,也可以通过OC的动态特性,使其最终调用别的方法,例如abc
方法,甚至,还可以调用另外一个类的方法。除此之外,OC还可以在程序运行阶段,给类增加方法等,这就是所谓的动态特性。
Runtime简介
- Objective-C是一门动态性比较强的编程语言,根 C、C++ 等语言有很大不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C/C++/ 汇编语言编写
isa详解
深入Runtime之前,先要解决一个比较重要的概念——isa
。在早期的Runtime里面,isa
指针直接指向class/meta-class
对象的地址,isa
就是一个普通的指针。
后来,苹果从ARM64位架构开始,对isa
进行了优化,将其定义成一个共用体(union
)结构,结合 位域 的概念以及 位运算 的方式来存储更多类相关信息。isa
指针需要通过与一个叫ISA_MASK的值(掩码)进行二进制&
运算,才能得到真实的class/meta-class
对象的地址。接下来,就具体探究一下苹果究竟是怎么优化的。
首先从源码角度,对比一下变化isa
优化前后的变化
***************************************
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
上面是64位之前,objc_object
的定义如上,isa直接指向objc_class
。
再看看优化后objc_object
的定义
struct objc_object {
private:
isa_t isa;
public:
arm64开始,isa
的类型变成了isa_t
,这是什么鬼?这个就是接下来讨论的重点,先看一下它的源码
union isa_t
{
isa_t() {
}
isa_t(uintptr_t value) : bits(value) {
}
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
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
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
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
// SUPPORT_PACKED_ISA
#endif
#if SUPPORT_INDEXED_ISA
# if __ARM_ARCH_7K__ >= 2
# define ISA_INDEX_IS_NPI 1
# define ISA_INDEX_MASK 0x0001FFFC
# define ISA_INDEX_SHIFT 2
# define ISA_INDEX_BITS 15
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
# define ISA_INDEX_MAGIC_MASK 0x001E0001
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t indexcls : 15;
uintptr_t magic : 4;
uintptr_t has_cxx_dtor : 1;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 7;
# define RC_ONE (1ULL<<25)
# define RC_HALF (1ULL<<6)
};
# else
# error unknown architecture for indexed isa
# endif
// SUPPORT_INDEXED_ISA
#endif
};
上面的代码就是苹果对于isa优化的精华所在,为了看懂上面的代码,首先需要从一些基础知识开始说。
场景需求分析
首先定义一个类CLPerson
,首先给CLPerson
增加几个属性以及成员变量
@interface CLPerson : NSObject
{
BOOL _tall;
BOOL _rich;
BOOL _handsome;
}
@property (nonatomic, assign, getter=isRich) BOOL rich;
@property (nonatomic, assign, getter=isTall) BOOL tall