Category的使用场合是什么
拆解模块
Category的实现原理
Category的底层结构是struct category_t 里面存储着分类的对象方法 类方法 属性 协议
在程序运行的时候 runtime会将Category的数据 合并到类信息中(类对象、元类对象中)
Category 和 Extension的区别是什么
Class Extension 在编译的时候 他的数据就已经包含在类信息中
Category 是在运行时 才会将数据合并到类信息中
Category有load方法吗? load方法是什么时候调用的? load方法能继承吗?
有load方法
load方法在runtime加载类 分类的时候调用
load方法可以继承 但是一般情况下 不会主动去调用load方法 都是让系统自动调用
load initialize方法的区别是什么 他们在category中的调用顺序是什么 以及出现继承的时候他们的调用过程
区别
1.调用方式
a.load是根据函数地址直接调用
b.initialize是通过objc_msgSend调用
2.调用时刻
a.load是runtime加载类 分类的时候调用
b.initialize是类第一次接受到消息的时候调用 每一个类只会initialize一次 (父类的initialize方法可能会被调用多次)
调用顺序
load
- 先调用类的load
a.先编译的类 优先调用load
b.调用子类的load之前 会先调用父类的load
- 再调用分类的load
a.先编译的分类 优先调用load
initialize
- 先初始化父类
- 再初始化子类 (可能最终调用的是父类的initialize方法)
Category能否添加成员变量 如果可以 如何给Category添加成员变量
不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果
Catgory的底层结构
- 编译时 所有分类的数据都会存放在_category_t的结构体中
- 一个分类就相当于一个结构体 有多少分类就有多少对应的_category_t结构体
- 运行时 通过runtime动态将分类的方法合并到类对象 元类对象中
- 最后参与编译的分类 放在最前面 他的方法在方法列表的最前面 最先被调用
Category的加载处理过程
1.通过Runtime加载某个类的所有Category数据
2.把所有Category的方法 属性 协议数据 合并到一个大数组中
后面参与编译的Category数据 会在数组的前面(先对数组进行扩容 原先的大小+分类里面的列表的大小 然后把原先
放在方法列表的里面往后挪动,挪动到最后 把前面的位置空出来,把分类里面的方法列表放在原先的方法列表的前面)
3.将合并后的分类数据 (方法 属性 协议) 插入到原来数据的前面
**最后编译的放在最前面 所以 会被先调用**
一个类只有一个类对象
memove memcopy区别
- memove 根据传进去的参数先判断往哪个方向挪动 判断挪数据是哪个数据先开始挪动(可以保证数据可以完整的挪动过去)
- memcopy 一个个的拷贝 从小地址开始 一个个的挪动
load方法
不管这个类是否被调用 都会加载这个load方法
+load 方法会在runtime加载类 分类时调用
每个类、分类的+load,在程序运行过程中只调用一次
load方法调用顺序
1.先调用类的+load
按照编译先后顺序调用(先编译,先调用)
调用子类的+load之前会先调用父类的+load
2.再调用分类的+load
按照编译先后顺序调用(先编译,先调用)
load方法的调用是根据方法地址直接调用 不是经过objc_msgSend函数调用
initialize方法
initialize方法 是类第一次接受到消息的时候调用
initialize方法调用顺序
- 先调用父类的+initialize,再调用子类的+initialize
- (先初始化父类,再初始化子类,每个类只会初始化1次)
如果分类实现了+ initialize 方法 就会覆盖类本事的initialize 调用
如果子类没有实现initialize 会调用父类的initialize (所以 父类的initialize可能会被调用多次)
initialize会先调用父类的initialize方法 是因为它内部有主动调用父类的initialize(有继承关系和父类还没有调用initialize方法的前提下)
关联对象
使用全局变量实现setter方法
缺点 因为全局变量的原因 会造成所有的对象都是一个属性值 值是一样的
使用全局的字典实现setter方法
缺点 线程安全 内存泄露问题 不同的线程访问字典出现线程安全
如果出现多个属性 就要出现多个字典 代码复杂
设置关联对象
设置key为const void *MJNameKey;
因为未设置值 就代表key是NULL 多个key都是NULL 出现相应问题
const void *MJNameKey;
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, MJNameKey);
}
设置key为const void *MJNameKey = &MJNameKey;
未设置为static 外部可以访问改变
const void *MJNameKey = &MJNameKey;
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, MJNameKey);
}
设置key为static const void *MJNameKey = &MJNameKey;
使得该key只能在当前文件使用
static const void *MJNameKey = &MJNameKey;
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, MJNameKey);
}
设置key为static const char MJNameKey;
只占用一个字节 节省空间
static const char MJNameKey;
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, &MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, &MJNameKey);
}
设置key为@"name"
字符串里面装的是地址值
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
设置key为宏定义
#define MJNameKey @"name"
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, MJNameKey);
}
设置key为IMP
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, @selector(name));
}
设置key为IMP 同时在getName里面设置为_cmd
- (void)setName:(NSString *)name{
//id _Nonnull object 对象 关联对象
//const void * _Nonnull key 指针
//id _Nullable value 关联的值
//objc_AssociationPolicy policy 关联策略
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, _cmd);
}
关联策略
关联对象的原理
关联对象的核心对象
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
其中object 是AssociationsHashMap的key 也就是disguised_ptr_t
key存放在ObjectAssociationMap里面 是里面的key
value policy 是在ObjcAssociation里面存放
关联对象并不是存储在被关联对象本身内存中 关联对象存储在全局的统一的一个AssociationsManager中
设置关联对象为nil,就相当于是移除关联对象