用多少眼泪才能让你相信 发表于 2024-8-18 20:45:48

【iOS】——分类,扩展以及关联对象

1. 分类(Category)和扩展(Extension)

1) 分类(运行期)



[*]分类就是为已经存在的类,在运行时添加新的方法的一种机制。
[*]分类可以添加协议,类方法,实例方法和属性,不能添加实例变量。
[*]添加的属性就不会生成成员变量,也不会生成setter/getter方法,必要手动添加setter/getter方法。
2)扩展(编译期)



[*] 扩展和分类很像,扩展只有声明部分,扩展中的定义的方法必要在类的实现部分去实现。
[*] 可以定义协议,类方法,实例方法,属性和实例变量,在编译期将此中的定义的数据加到该类的数据类表中。假如不实现此中的方法,就会报错。
[*] 由于系统的类的是实现部分不对用户开放,以是不能给系统的类添加扩展。
3)区别



[*]扩展在编译期决定,是类的一部分,和类的声明部分和实现部分共同组成类,陪同着类的产生而产生,消亡而消亡。扩展也可以用来潜伏类的私有部分。
[*]分类是在运行期决议的,扩展可以添加实例变量,分类不能添加实例变量,原因是在运行时,对象的内存结构就已经确定好了,假如添加实例变量,会粉碎类的内存结构。
2. 分类的实质

1)分类原理

typedef struct category_t *Category;
struct category_t {
    const char *name; // 分类名
    classref_t cls; // 类
    struct method_list_t *instanceMethods;// 实例方法
    struct method_list_t *classMethods; // 类方法
    struct protocol_list_t *protocols;// 协议
    struct property_list_t *instanceProperties; // 实例属性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;   // 类属性

    method_list_t *methodsForMeta(bool isMeta) {
      if (isMeta) return classMethods;
      else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

发现里面存在实例方法列表,类方法列表,实例属性列表与类属性列表。
2)分类的加载流程


[*]在编译阶段将分类中的方法、属性等编译到一个数据结构category_t。
[*]将分类中的方法、属性等归并到一个大数组中,反面参加编译的分类会在数组的前面。
[*]将归并后的分类数据(方法、属性、协议),插入到类原来数据的前面。
也就是说当分类中的方法与原始类中的方法重名时,会先去调用分类中实现的方法。
2. 关联对象

通过关联对象给分类添加属性
动态添加

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)



[*] 参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
[*] 参数二:void * == id key : key值,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key得到属性的值并返回。
[*] ** 参数三:**id value : 关联的值,也就是set方法传入的值给属性去生存。
[*] 参数四:objc_AssociationPolicy policy : 策略,属性以什么形式生存。
取值

objc_getAssociatedObject(id object, const void *key);



[*]参数一:id object : 获取哪个对象里面的关联的属性。
[*]参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出valu
移除关联对象

- (void)removeAssociatedObjects
{
    // 移除关联对象
    objc_removeAssociatedObjects(self);
}

应用

//分类.h文件
#import "LGPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson (cat)
@property (nonatomic, copy) NSString *lg_nickname;
@property (nonatomic, copy) NSString *name;
@end

NS_ASSUME_NONNULL_END

//分类.m文件
#import "LGPerson+cat.h"
#import <objc/runtime.h>
@implementation LGPerson (cat)
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *) name {
    return objc_getAssociatedObject(self, _cmd);
}
@end


//main文件
#import <Foundation/Foundation.h>
#import "LGPerson+cat.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
      
      LGPerson *t = [ init];
      t.name = @"1";
      NSLog(@"%@", t.name);
      
    }
    return 0;
}
运行结果:
https://i-blog.csdnimg.cn/direct/c2d7bafd9842430197c12a36f4a950ba.png
总结:


[*]分类中没有成员列表,因此不能添加成员变量。但是有属性列表,可以添加属性的声明,但是不会合成set与get方法,假如要利用分类中的属性,必要利用关联对象。
[*]分类在运行的时间被整合到类中,扩展在编译的时间被整合到类中,因此分类中的方法不实现不会报错,扩展就会报错。
[*]扩展用于声明私有属性与方法。
[*]分类中的方法和类中的方法重名,分类中的方法会取代类中的方法。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【iOS】——分类,扩展以及关联对象