Category

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的底层结构

Catgory的底层结构.png
- 编译时 所有分类的数据都会存放在_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);
}

关联策略

关联策略.png

关联对象的原理

关联对象的核心对象
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
关联对象的核心对象.png
关联对象的核心对象之间的关系.png
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,就相当于是移除关联对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容