美文网首页
浅谈Runtime(二)

浅谈Runtime(二)

作者: 小宇pod | 来源:发表于2018-05-13 19:17 被阅读0次

如有不同看法,欢迎讨论。
这里主要是讲Runtime在项目中的一些常用方法。其实讲这个的文章已经很多了,这里之所以记录一下,是方便自己以后查找。

一、方法交换

在开发中常遇到的一个场景就是当导入某个三方包或系统的原有方法不能满足需求的时候,或当改动量过大的时候,可以考虑将原有方法替换重写。这里就应用到了Runtime将方法替换。
在这里用了一个例子,替换并重写系统方法:
1.创建category。
2.在load方法中通过Runtime取出要替换的方法并进行替换。

category中的方法实现
+ (void)load{

    Method method1 = class_getClassMethod(self, @selector(imageNamed:));

    Method method2 = class_getClassMethod(self, @selector(imageNamed_CY:));
    NSLog(@"iiiiiiii");
    method_exchangeImplementations(method1, method2);
//    objc_class
}
+ (UIImage *)imageNamed_CY:(NSString *)imageName{
    UIImage *img = [UIImage imageNamed_CY:imageName];
    if(img){
        NSLog(@"图片存在");
    }else{
        NSLog(@"图片不存在");
    }
    return img;
}
在需要调用的地方调用方法
    UIImageView *image = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"WechatIMG16314.jpeg"]];

打印结果


WechatIMG155.jpeg

这里要多提一下load方法,在一个程序开始运行之前(在main函数开始执行之前),在库开始被程序加载,load函数就会开始被执行。所以在category中可以在load方法里置换方法。

二、动态添加属性

面试中一个常见问题就是category可不可以添加属性。这里考察的是在category中能否动态为我们生成set/get方法。当我们直接在category中声明属性或成员变量时,系统是不会主动为我们添加set/get方法。这就也就是为什么当直接category中声明属性或成员变量时,会报错:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImage setArr:]: unrecognized selector sent to instance 0x6000000b3e00'

上边的报错就是在UIImage的category中直接声明了一个属性arr,并赋值。
但实际上,通过Runtime是可以在category中自己添加set/get方法以达到动态的添加属性的目的。实现方式:

- (void)setArr:(NSArray *)arr{
    //  参数1给哪个对象添加属性
    //  参数2属性名称
    //  参数3属性对应的值
    //  安全策略,即我们常用的strong等等的宏定义
    objc_setAssociatedObject(self, @"arr", arr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSArray *)arr{
   return objc_getAssociatedObject(self, @"arr");
}

点进OBJC_ASSOCIATION_RETAIN_NONATOMIC可以看见

/**
 * Policies related to associative references.
 * These are options to objc_setAssociatedObject()
 */
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

添加不同的属性时选择不同的Policies。

三、实现NSCoding的自动归档和解档

像平时的归档操作,如果一个对象有多个属性时,我们需要对每一个对象进行encodeObject 和 decodeObjectForKey操作,即

@interface MyClass : NSObject<NSCoding>
- (void)showAge;
- (void)showName:(NSString *)name;
@property (nonatomic,copy) NSString *pro1;
@property (nonatomic,copy) NSString *pro2;
@property (nonatomic,copy) NSString *pro3;

@end
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_pro1 forKey:@"pro1"];
    [aCoder encodeObject:_pro2 forKey:@"pro2"];
    [aCoder encodeObject:_pro3 forKey:@"pro3"];
    
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.pro1 = [aDecoder decodeObjectForKey:@"pro1"];
        self.pro2 = [aDecoder decodeObjectForKey:@"pro2"];
        self.pro3 = [aDecoder decodeObjectForKey:@"pro3"];
    }
    return self;
}

如果属性过多的时候,这么写就很蛋疼。应用Runtime,我们可以用更简洁的代码实现归档操作。

- (void)encodeWithCoder:(NSCoder *)encoder

{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([MyClass class], &count);
    
    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}



- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([MyClass class], &count);
        for (int i = 0; i<count; i++) {
            // 取出i位置对应的成员变量
            Ivar ivar = ivars[i];
            // 查看成员变量
            const char *name = ivar_getName(ivar);
            // 归档
            NSString *key = [NSString stringWithUTF8String:name];
            id value = [aDecoder decodeObjectForKey:key];
            // 设置到成员变量身上
            [self setValue:value forKey:key];
            
        }
        free(ivars);
        
    }
    return self;
}

这样和之前的办法比就简洁且通用。

四、总结

实际上除了以上几种之外,Runtime给我们提供了丰富的接口,比如获取协议列表,获取类方法,实例方法,替换原方法,交换方法,等等等等。这种东西是可以轻易查到的,在此就不列举了。总体来说,Runtime简单又强大,是OC动态语言的一个展示和应用,在很多情况下,通过Runtime可以减少不必要的工作量。大体Runtime就先到这了,如果以后有不同理解,会再来补。

相关文章

  • 浅谈runtime关联

    浅谈runtime关联 浅谈runtime关联

  • 浅谈Runtime(二)

    如有不同看法,欢迎讨论。这里主要是讲Runtime在项目中的一些常用方法。其实讲这个的文章已经很多了,这里之所以记...

  • Runtime 浅谈

    Objective-C 的 Runtime 铸就了它动态语言的特性,这些深层次的知识虽然平时写代码用的少一些,但是...

  • Runtime浅谈

    oc/swift是一种类面向对象语言c是一种结构化的语言 1:为什么我们使用oc和c很容易融合一起编写代码?下面可...

  • iOS-万能的Runtime(2):基于Runtime机制解决线

    万能的Runtime(1):《iOS-浅谈NSUserDefaults保存数据的缺点以及改进方案:UDUserDe...

  • 浅谈Filecoin(二)

    浅谈Filecoin(二) 浅谈Filecoin(一)链接:浅谈Filecoin(一) (Verifiable)M...

  • iOS runtime(三)runtime之method(1)m

    iOS runtime(一)runtime之Property 详尽iOS runtime(二)runtime之Iv...

  • 浅谈Runtime(一)

    如有不同看法,欢迎讨论。 一. RunTime是什么 runtime(简称运行时),是一套 纯C(C和汇编写的) ...

  • 浅谈runtime关联

    目录 序:面试中并不绝对的答案 开篇 核心代码 实例二则 1、序:面试中并不绝对的答案     今儿听到一个哥们讲...

  • iOS浅谈-runtime

    RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时...

网友评论

      本文标题:浅谈Runtime(二)

      本文链接:https://www.haomeiwen.com/subject/odlhdftx.html