a) 无论你有多少个分类 分类不同模块里面的方法 他都会合并到类对象/ 元类对象的方法列表里面
b) 合并的操作 不是编译的时候 直接咔嚓剪切过去,而是程序运行的时候通过runtime机制实现
c) 编写多少个分类实则就是有多少个struct _category_t结构体对象
d) 编译顺序 取决于compile sources顺序越是往后参与编译的分类那么分类的方法越会被先调用
a) 定义在objc- runtime- new. h中
a) 通过Runtime加载某个类的所有Category数据
b) 把所有Category的方法、属性、协议数据,合并到一个大数组中
1 ) 后面参与编译的Category数据,会在数组的前面
c) 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
d) 源码解读顺序
objc- os. mm ( runtime的入口)
_objc_init
map_images
map_images_nolock
objc- runtime- new. mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、memcpy
4.Category和Class Extension的区别是什么
a) Class Extension也就是我们在. m文件中@interface 申明的东西, 在编译的时候,它的数据就已经包含在类信息中
b) Category是在运行时,才会将数据合并到类信息中
a) memmove比memcpy多一层判断,能够保证数据的完整性
a) + load方法会在runtime加载类、分类时调用
b) 每个类、分类的+ load,在程序运行过程中只调用一次
c) 调用顺序
1 ) 先调用类的+ load
1.1 ) 按照编译先后顺序调用(先编译,先调用)
1.2 ) 调用子类的+ load之前会先调用父类的+ load
2 ) 再调用分类的+ load
2.1 ) 按照编译先后顺序调用(先编译,先调用)
d) objc4源码解读过程:objc- os. mm
_objc_init
load_images
prepare_load_methods
schedule_class_load
add_class_to_loadable_list
add_category_to_loadable_list
call_load_methods
call_class_loads
call_category_loads
( * load_method) ( cls, SEL_load)
e) load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用
a) + initialize方法会在类第一次接收到消息时调用
b) 调用顺序
1 ) 先调用父类的+ initialize,再调用子类的+ initialize ( 子类不一定会调用)
2 ) 先初始化父类,再初始化子类,每个类只会初始化1 次
c) + initialize和+ load的很大区别是,+ initialize是通过objc_msgSend进行调用的,所以有以下特点
1 ) 如果子类没有实现+ initialize,会调用父类的+ initialize(所以父类的+ initialize可能会被调用多次)
2 ) 如果分类实现了+ initialize,就覆盖类本身的+ initialize调用
d) objc_msgSend底层是通过汇编实现的
e) objc4源码解读过程
objc- msg- arm64. s
objc_msgSend
objc- runtime- new. mm
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend ( cls, SEL_initialize)
8.Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?
a) 有load方法
b) load方法在runtime加载类、分类的时候调用
c) load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用
9.load、initialize方法的区别什么?
a) 调用方式
1 ) load是根据函数地址直接调用
2 ) initialize是通过objc_msgSend调用
b) 调用时刻
1 ) load是runtime加载类、分类的时候调用( 只会调用1 次)
2 ) initialize是类第一次接到消息的时候调用,每一个类只会initialize一次( 父类的initialize方法可能会调用多次)
c) 调用顺序
1 ) load
1.1 ) 先调用类的load
1.1 .1 ) 先编译的类,优先调用
1.1 .2 ) 调用子类的load之前,会调用父类的load
1.2 ) 再调用分类的load
1.2 .1 ) 先编译的分类,优先调用
2 ) initialize
2.1 ) 先初始化父类
2.2 ) 再初始化子类( 可能最终调用的是父类的initialize方法)
a) 我们在类中写一条类似@property ( assign, nonatomic) int age的属性会帮我们做3 件事
1 ) 生成一条_age的成员变量
2 ) 申明setter& getter方法
3 ) 实现setter& getter方法
b) 我们在分类中写一条类似@property ( assign, nonatomic) int age的属性只会帮我们做1 件事
1 ) 申明setter& getter方法
a) 默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接实现
b) 关联对象提供了以下API
1 ) 添加关联对象
void objc_setAssociatedObject ( id object, const void * key, id value, objc_AssociationPolicy policy)
2 ) 获得关联对象
id objc_getAssociatedObject ( id object, const void * key)
3 ) 移除所有的关联对象
void objc_removeAssociatedObjects ( id object)
12.模拟给Category“添加成员变量”中key的常见用法
a) 使用全局的int weight_属性来实现,问题当有多个实例对象的时候,修改的值始终为最后一次调用
- ( void ) test1
{
int weight_;
- ( void ) setWeight: ( int ) weight
{
weight_ = weight;
}
- ( int ) weight
{
return weight_;
}
- ( void ) setWeight: ( int ) weight
{
_weight = weight;
}
- ( int ) weight
{
return _weight;
}
}
b) 使用字典来实现,存在内存泄漏,多线程的问题
- ( void ) test2
{
# define MJKey [ NSString stringWithFormat: @ "%p" , self ]
NSMutableDictionary * names_;
NSMutableDictionary * weights_;
+ ( void ) load
{
weights_ = [ NSMutableDictionary dictionary] ;
names_ = [ NSMutableDictionary dictionary] ;
}
- ( void ) setName: ( NSString * ) name
{
names_[ MJKey] = name;
}
- ( NSString * ) name
{
return names_[ MJKey] ;
}
- ( void ) setWeight: ( int ) weight
{
weights_[ MJKey] = @ ( weight) ;
}
- ( int ) weight
{
return [ weights_[ MJKey] intValue] ;
}
}
c) 使用objc_setAssociatedObject来实现
- ( void ) test3
{
- ( void ) setName: ( NSString * ) name
{
objc_setAssociatedObject ( self , @selector ( name) , name, OBJC_ASSOCIATION_COPY_NONATOMIC) ;
}
- ( NSString * ) name
{
return objc_getAssociatedObject ( self , _cmd) ;
}
- ( void ) setWeight: ( int ) weight
{
objc_setAssociatedObject ( self , @selector ( weight) , @ ( weight) , OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
- ( int ) weight
{
return [ objc_getAssociatedObject ( self , _cmd) intValue] ;
}
}