IOS-Foundation-runtime使用举例

本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。

上一节,我介绍了runtime的基本概念,如有不清楚的地方,请猛点这里

这一节,举例说明runtime的常用使用场合。


一. 动态获取一个类的属性列表信息


首先定义一个Person类,代码如下:

@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) BOOL gender;
@end

然后就可以使用runtime机制动态的获取属性信息了。代码如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        // 1.获取类信息
        id personClass = objc_getClass("Person");
        
        unsigned int outCount,i;
        objc_property_t *properties =  class_copyPropertyList(personClass, &outCount);
        
        // 2. 获取属性信息
        for(i=0;i<outCount;i++){
            objc_property_t property = properties[i];
            NSLog(@"Name:%s;Attribute:%s",property_getName(property),property_getAttributes(property));
        }
        
    }
    return 0;
}

需要注意的是,需要导入头文件 #import<objc/runtime.h>, 因为 objc_property_t,property_getName 这些方法都是 #import <objc/runtime.h> 动态库中提供的


二. 用runtime实现归档解档


一般的实现方式:(弊端:1. 必须针对指定的类写类似的代码;2. 如果属性比较多的话,代码量就比较大)

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeBool:self.gender forKey:@"gender"];
    [aCoder encodeInteger:self.age forKey:@"age"];
    
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if(self = [super init])
    {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.gender = [aDecoder decodeBoolForKey:@"gender"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}

runtime的实现方式

// 解档
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if(self = [super init])
    {
        unsigned int count;
        Ivar *ivars = class_copyIvarList([Person class], &count);
        for(int i=0;i<count;i++){
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);
            NSString *strName = [NSString stringWithUTF8String:name];
            // 进行解档取值
            id value = [aDecoder decodeObjectForKey:strName];
            // 利用KVC对属性赋值
            [self setValue:value forKey:strName];
        }
    }
    return self;
}

// 归档
-(void)encodeWithCoder:(NSCoder *)aCoder
{
    unsigned int count;
    Ivar *ivars = class_copyIvarList([Person class], &count);
    for(int i=0;i<count;i++){
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *strName = [NSString stringWithUTF8String:name];
        // 利用KVC取值
        id value = [self valueForKey:strName];
        // 进行归档赋值
        [aCoder encodeObject:value forKey:strName];
        
    }
}


也许大家对上面所涉及到的很多runtime方法有点陌生 ,点击这里 可以查看详细使用列表。


三.  Swizzle

在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。

每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。  

 

我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,

我们可以利用 class_replaceMethod 来修改类,

我们可以利用 method_setImplementation 来直接设置某个方法的IMP,
……

归根结底,都是偷换了selector的IMP,如下图所示:



上面的图解释的也许有点抽象了,下面举一个具体来说明。

假如你现在的项目有一个需求,项目中所有的图片全部要跟新为UI部门所设计的新图片(而且图片名称全部以"_os8")结尾,这个时候,也许开发人员就要奔溃了! 全局搜索  "imageNamed:"方法,然后在图片名称后面全部加上"_os8"后缀,可想而知,这纯粹是体力活啊!

这个时候,我们就可以考虑使用Swizzle了,大致实现代码如下:

@implementation UIImage (Extension)
/**
 *  分类被装载到内存中,就会调用1次
 */
+(void)load {
    Method older = class_getClassMethod(self, @selector(imageNamed:));
    Method newer = class_getClassMethod(self, @selector(lFImageNamed:));
    // 交换两个方法
    method_exchangeImplementations(older, newer);
}

+ (UIImage *)lFImageNamed:(NSString *)name {
    BOOL iOS8 = [[UIDevice currentDevice].systemVersion floatValue] >= 8.0;
        
    if(iOS8) {
         name = [name stringByAppendingString:@"_os8"];
    }
    
    return [self lFImageNamed:name];
}
@end


写一个UIImage的分类就可以了。上面的代码需要解释的地方是: 当调用
[self lFImageNamed:name]
这一句的时候,大家也许会认为造成死循环。其实不然,它其实调用的是 imageName: 方法,只不过偷偷的将传进来的参数名称加上了_os8, 之所以可以进行方法的交换操作,是因为
method_exchangeImplementations
这个方法,它完成了 lFImageNamed: 与 imageNamed: 方法的交换工作。

关于runtime的使用就介绍这么多,下一节,我为大家介绍一个基于runtime实现的

字典模型转换插件“MJExtension”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋恨雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值