运行时

很多人都喜欢研究底层的东西, 因个人比较low, 只能讲讲runtime在实际工作中的应用.

  • 应用1 动态获取类的属性
// 获取成员变量列表, 第三方框架使用此方法居多
// 参数1: 要copy的类
// 参数2: 属性计数指针
class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount);

// 获取方法列表
class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount);

// 获取属性列表
class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount);

// 获取协议列表
class_copyProtocolList(__unsafe_unretained Class cls, unsigned int *outCount)

举例: 这种方式可以动态获取一个类中的属性; 可以避免字典和模型中属性不匹配时造成崩溃

+ (NSArray *)loadProperties{
    unsigned int count = 0;
    // 返回所有属性的数组
    // C语言中, 指向数组第一个元素的是一个指针
    objc_property_t *list = class_copyPropertyList([self class], &count);

    NSLog(@"属性数量 %u", count);

    // 遍历数组
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; ++i) {
        // C语言中没有对象的概念,一般不需要使用 `*`
        objc_property_t pty = list[i];

        // 属性名称
        const char *cname = property_getName(pty);

        // 添加到数组
        [arrayM addObject:[NSString stringWithUTF8String:cname]];
    }
    NSLog(@"%@", arrayM);

    // 释放对象
    // 用C语言方法创建对象, 用到copy/new/retain/create等时要释放对象, 具体使用什么释放, 看文档
    free(list);

    return arrayM.copy;
}
  • 应用2 关联对象
    使用: 在第三方框架使用特别多, 目的是让分类解耦, 动态给某些类增加属性做缓存

上面的例子可以解决动态添加属性的问题, 但是每一次调用loadProperty方法时都会进来一次; 而程序运行起来, 类的属性不会再变, 为了提升性能, 我们可以考虑只提取一次; 因此我们需要使用到关联对象

const char *kPropertiesKey = "kPropertiesKey";
+ (NSArray *)loadProperties{

    // 利用`关联`对象,给`类`添加属性,OC中的类,本身就是一个特殊对象
    /**
     获取关联对象
     1. 对象,属性关联到的对象
     2. key,属性的 key
     */
    NSArray *pList = objc_getAssociatedObject(self, kPropertiesKey);
    if (pList != nil) {
        return pList;
    }
    unsigned int count = 0;
    // 返回所有属性的数组
    // C语言中, 指向数组第一个元素的是一个指针
    objc_property_t *list = class_copyPropertyList([self class], &count);

    NSLog(@"属性数量 %u", count);

    // 遍历数组
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; ++i) {
        // C语言中没有对象的概念,一般不需要使用 `*`
        objc_property_t pty = list[i];

        // 属性名称
        const char *cname = property_getName(pty);

        // 添加到数组
        [arrayM addObject:[NSString stringWithUTF8String:cname]];
    }
    NSLog(@"%@", arrayM);

    // 释放对象
    // 用C语言方法创建对象, 用到copy/new/retain/create等时要释放对象, 具体使用什么释放, 看文档
    free(list);

    // 设置关联对象对象
    /**
     设置关联对象属性,运行时机制中,在OC开发的应用,关联对象使用的频率最高!

     1. 属性关联的对象
     2. key
     3. 值
     4. 引用关系
     */
    objc_setAssociatedObject(self, kPropertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);

    return objc_getAssociatedObject(self, kPropertiesKey);
}
  • 应用3 交换方法–拦截系统或其他框架的方法

原理: 方法在内存中调用是通过函数指针, 系统有一个函数映射表, 里面记录所有函数的地址, 当调用某个函数时, 函数指针指向这个函数的地址;

应用: AFN中有一个方法 resume表示开始网络任务, 我想要在网络任务开启后发一个通知, 这样就可以监听到网络任务开启了;
但是resume是苹果封装好的, 我们够不到苹果底层东西. 怎么办?
我们定义一个方法 如 AF_Resume 开启网络任务同时发送通知; 但是我们开发一个框架时, 不可能告诉每个人都使用AF_Resume;
这时我们就需要用到交换方法; 函数映射表中记录着resume和AF_Resume两个函数的地址, 直接交换两个函数的地址; 交换后再调用resume时, 其实已经去执行 AF_Resume了.

  • 给分类添加成员变量

创建一个分类UIImageView+WebImage, 设置一个URLString属性, 在implementation中重写set和get方法.

// MARK: - 运行时关联对象
const void *HMCurrentURLStringKey = "HMCurrentURLStringKey";

- (void)setCurrentURLString:(NSString *)currentURLString {
    objc_setAssociatedObject(self, HMCurrentURLStringKey, currentURLString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)currentURLString {
    return objc_getAssociatedObject(self, HMCurrentURLStringKey);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值