iOSruntime运行时的四种基本应用场景

本文介绍了Objective-C运行时(runtime)的概念及其应用场景,包括方法交换、动态添加属性、字典转换为模型对象及归档解档操作。通过实例展示了如何利用runtime增强程序的灵活性。

1:什么是运行时(runtime)?

      因为Objective-C是一门动态型语言,所以会把一些决定工作本来在编译期完成的,放在运行的时候去做。这样做的目的极大的增加的系统的灵活性。所以编译器是不够的,我们还需要一个运行时系统 (runtime system) 来执行在运行的时候需要执行的代码。这就是 Objective-C Runtime 系统存在的意义,它是整个OC运行框架的一块基石。在这里不做运行时底层的实现做说明,只总结一下运行时在应用中的4种基本场景。

2:在什么场合使用运行时(runtime)?

    (1):交换方法

      1:导入头文件

#import <objc/runtime.h>
      2:实现自定义的方法

+(void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{
    
    Class class = [self class];
    
    //原始方法
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    //替换方法
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    //参数1:添加方法的类
    //参数2:已经被添加的方法
    //参数3:将要添加的新方法,该方法必须有两个参数和_cmd;
    //参数4:描述方法类型的字符类型
    //如果被添加成功就会返回successful,如果这个类已经有相同函数名的方法,并且已经被实现,那么就会返回NO
    //如果想要改变这个已经存在的方法,就使用method_setImplementation方法
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    /**
     * Replaces the implementation of a method for a given class.
     *
     * @param cls The class you want to modify.
     * @param name A selector that identifies the method whose implementation you want to replace.
     * @param imp The new implementation for the method identified by name for the class identified by cls.
     * @param types An array of characters that describe the types of the arguments to the method.
     *  Since the function must take at least two arguments—self and _cmd, the second and third characters
     *  must be “@:” (the first character is the return type).
     *
     * @return The previous implementation of the method identified by \e name for the class identified by \e cls.
     *
     * @note This function behaves in two different ways:
     *  - If the method identified by \e name does not yet exist, it is added as if \c class_addMethod were called.
     *    The type encoding specified by \e types is used as given.
     *  - If the method identified by \e name does exist, its \c IMP is replaced as if \c method_setImplementation were called.
     *    The type encoding specified by \e types is ignored.
     */
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        
    }else{
        method_exchangeImplementations(originalMethod, swizzledMethod);
        
    }
    
}
   

      3:应用
              (1:导入上面的头文件

#import "NSObject+RuntimeMethodSwizzling.h"

         (2:  重写父类方法

+(void)load{
 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        [UIViewController methodSwizzlingWithOriginalSelector:@selector(viewWillAppear:) bySwizzledSelector:@selector(my_ViewWillAppear:)];
    });

}

         (3:  实现替换的方法

-(void)my_ViewWillAppear:(BOOL)animation{
    
    [self my_ViewWillAppear:animation];
    
    NSLog(@"父类交换了方法");
    
    //注意:新的方法应该总是在+load方法中
    //在OC中 ,RunTime 会在类初始化加载的时候调用+laod方法,在类的第一次被调用的时候实现
    //initialize方法,由于Method Swizzling 会影响到类的全局状态,所以要尽量避免在并发处理
    //竞争情况 ,+load方法能保证在类的初始化的过程中被加载,并保证这种改变应用级别的一致性
    //要使用安全单利进行交换
    //
    
}

      (2):添加属性

          (1:为UIImageView 增加一个实例变量

#import <UIKit/UIKit.h>

@interface UIImageView (AddProperty)

@property (nonatomic,copy)NSString * downUrl;

@end

             (2:重写setter和getter方法

#import <objc/runtime.h>

//添加属性
-(void)setDownUrl:(NSString *)downUrl{
    
    objc_setAssociatedObject(self, @selector(downUrl), downUrl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}
-(NSString *)downUrl{
    
    return objc_getAssociatedObject(self, @selector(downUrl));
    
}

            (3:引用添加的属性

 UIImageView * imageview = [[UIImageView alloc] init];
    
    //添加一个属性
    imageview.downUrl = @"www.baidu.com";
    
    imageview.frame = CGRectMake(100, 200, 100, 100);
    
    imageview.backgroundColor = [UIColor redColor];
    
    [self.view addSubview:imageview];

      (3) 字典转模型

#import <Foundation/Foundation.h>

@interface RunTime : NSObject
@property (nonatomic,copy)NSString * year;
@property (nonatomic,copy)NSString * month;
@property (nonatomic,copy)NSString * day;

+(id)createModelWith:(NSDictionary *)dictionary;
@end

        (1:实现方法

#import "objc/runtime.h"
#import "objc/message.h"

+(id)createModelWith:(NSDictionary *)dictionary{

    id objc = [[self alloc] init];
    
    unsigned int count = 0;
    
    if (objc) {
        
        Ivar * ivars = class_copyIvarList([self class], &count);
        
        for (int i = 0; i<count; i++) {
            
            Ivar ivar = ivars[i];
            
            //获取变量名称
            const char * name = ivar_getName(ivar);
            
            //获取变量名
            NSString * key = [NSString stringWithUTF8String:name];
            
            //下面的几步是用来生成setter方法
            NSString * keyvalue = [key substringFromIndex:1];
            
            key = keyvalue.capitalizedString;
            
            //拼接seter方法
            key = [NSString stringWithFormat:@"set%@:",key];
            
            SEL  func = NSSelectorFromString(key);
            
            //如果自己响应实例变量的setter方法
            if ([objc respondsToSelector:func]) {
                
                id value = @"";
                
                if ([dictionary objectForKey: keyvalue] != nil) {
                    
                    value = [dictionary objectForKey:keyvalue];
                    
                }
                
                //然后在主线程中执行方法
                [objc performSelectorOnMainThread:func withObject:value waitUntilDone:[NSThread isMainThread]];
            }
        }
        
        free(ivars);
    }
    
    return objc;
    
}

        (2:调用

//仿网络请求的数据
    NSDictionary * modeldict = @{@"year":@"2020",@"month":@"11",@"day":@"11"};
 
    RunTime * model = [RunTime createModelWith:modeldict];
    
    NSLog(@"解析数据:年:%@||月:%@||日:%@",model.year,model.month,model.day);

      (4)解档归档

        (1:实例变量

#import <Foundation/Foundation.h>

@interface EncodeAndUnEncode : NSObject<NSCoding>

@property (nonatomic,copy)NSString * title; //电影名
@property (nonatomic,copy)NSString * genres; //电影的种类
@property (nonatomic,copy)NSString * imageUrl;//电影的图片

//归档
-(void)encodeWithCoder:(NSCoder *)aCoder;

//解档
-(instancetype)initWithCoder:(NSCoder *)aDecoder;

@end

        (2:方法的实现

#import "EncodeAndUnEncode.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation EncodeAndUnEncode

//解
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    
    if (self = [super init]) {
        
        unsigned int count = 0;
        
        Ivar * ivarlist = class_copyIvarList([self class], &count);
        
        for (int i = 0; i<count; i++) {
            
            Ivar alvar = ivarlist[i];
            const char * name = ivar_getName(alvar);
            
            id value = [aDecoder decodeObjectForKey:[NSString stringWithUTF8String:name]];
            
            if (!value) {
                
            }else{
                
                [self setValue:value forKey:[NSString stringWithUTF8String:name]];
                
            }
            
        }
        free(ivarlist);
    }
    return self;
}
//归
-(void)encodeWithCoder:(NSCoder *)aCoder{
    
    //获取某个类的成员变量
    unsigned int count = 0;
    Ivar * ivarlist = class_copyIvarList([self class], &count);
    
    for (int i = 0; i < count; i ++) {
        
        Ivar alvar = ivarlist[i];
        
        //获取成员变量的名称
        const char * ivarname = ivar_getName(alvar);
        
        id value = [self valueForKey:[NSString stringWithUTF8String:ivarname]];
        
        if (!value) {
            
        }else{
            [aCoder encodeObject:value forKey:[NSString stringWithUTF8String:ivarname]];
        }
        
    }
    free(ivarlist);
    
}

        (2:调用

//归档的路径
    //如果不存在
    NSString* docPatn = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    NSString* path = [docPatn stringByAppendingPathComponent:@"moive.archiver"];
    
    //自定义对象存到文件中
    [NSKeyedArchiver archiveRootObject:moive toFile:path];
    
    
    //解档
    EncodeAndUnEncode * unmoive = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    
    NSLog(@"获取电影的信息:电影:%@||电影类别:%@||电影地址:%@",unmoive.title,unmoive.genres,unmoive.imageUrl);

以上就是runtime在应用的基本场景,防止忘记,记录一下


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值