OC的消息机制
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
平时编写的OC代码,底层都是转成Runtime API进行调用
OC中的方法调用其实是转成objc_msgSend函数的调用,给receiver(方法调用者)发送一条消息(selector方法名)
objc_msgSend底层有3大阶段
1.消息发送(当前类、父类中查找)
2.动态方法解析
3.消息转发
Runtime作用
利用关联对象(AssociatedObject)给分类添加属性
遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
交换方法实现
利用消息转发机制解决方法找不到的异常问题
isa详解
> nonpointer
0:代表普通的指针,存储着Class、Meta-Class对象的内存地址
1:代表优化过,使用位域存储更多的信息
>has_assoc
是否有设置过关联对象,如果没有,释放时会更快
>has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
>shiftcls
存储着Class、Meta-Class对象的内存地址信息
>magic
用于在调试时分辩对象是否未完成初始化
>weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快
>deallocating
对象是否正在释放
>extra_rc
里面存储的值是引用计数器减一
>has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
class_rw_t
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含类的初始类容,分类的内容
struct method_t{
SEL name; //函数名
const char *types; //编码(返回值类型,参数类型)
IMP imp; //指向函数的指针(函数地址)
}
>IMP代表函数的具体实现
typedef id _Nullable (*IMP)id _Nullable, SEL _Nonnull,...);
>SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似
1.可以通过@selector()和sel_registerName()获得
2.可以通过sel_getName()和NSStringFromSelector()转成字符串
3.不同类中相同名字的方法,所对应的选择器是相同的
typedef struct objc_selector *SEL;
struct class_rw_t{
method_array_t methods; // -> methods:method_list_t数组 method_list_t:method_t数组
property_array_t properties;
protocol_array_t protocols;
}
cache_t
Class内部结构中有个方法缓存(cache_t),用==散列表(哈希表)==来缓存曾经调用过的方法,可以提高方法查找速度
struct bucket_t{
cache_key_t _key; // SEL作为key
IMP _imp; //函数地址
}
struct method_t{
struct bucket_t *_buckets; //散列表
mask_t _mask;//散列表长度-1
mask_t _occupied;//已经缓存的方法数量
}
//缓存查找
bucket_t *cache_t::find(cache_key_t k, id receiver)
Super本质
super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数「struct objc_super2、SEL」
struct objc_super2 {
id receiver; //消息接受者
Class current_class;//receiver的Class对象
};
/**
[super message]的底层实现
1.消息接受者仍然是子类对象
2.从父类开始查找方法的实现
*/
struct objc_super {
__unsafe_unretained _Nonnull id receiver; //消息接受者
__unsafe_unretained _Nonnull Class super_class;// 消息接收者父类
};
- (void)run
{
//super 调用的receiver仍然是当前类对象
[super run];
}
类似于isa存储的位域使用
掩码,一般用来按位与(&)运算的
//#define CJTallMask 1
//#define CJRichMask 2
//#define CJHandsomeMask 4
//
//#define CJTallMask 0b00000001
//#define CJRichMask 0b00000010
//#define CJHandsomeMask 0b0000100
#define CJTallMask 1<<0
#define CJRichMask 1<<1
#define CJHandsomeMask 1<<2
union Date {
int year;
int month;
int day;
};
@interface CJPerson ()
{
// char _tallRichHandsome;//0b 0000 0000
//位域
struct{
char tall : 1;
char rich : 1;
char handsome : 1;
} _tallRichHandsome;
// //联合体(可读性高)
// union {
// char bits;//0b 0000 0000
// //位域
// struct{
// char tall : 1;
// char rich : 1;
// char handsome : 1;
// };
// } _tallRichHandsome;//实例对象
}
@end
@implementation CJPerson
- (instancetype)init
{
self = [super init];
if (self) {
// _tallRichHandsome = 0b00000000;
}
return self;
}
- (void)setIsRich:(BOOL)isRich
{
if (isRich) {
_tallRichHandsome.rich = _tallRichHandsome.rich|CJRichMask;
}else{
_tallRichHandsome.rich = _tallRichHandsome.rich&(~(CJRichMask));
}
}
- (BOOL)isRich{
return _tallRichHandsome.rich&CJRichMask;
}
@end