RunTime 运行时研究及解析

本文介绍Objective-C运行时的应用技巧,包括方法交换、动态添加属性、实现NSCoding协议及动态添加方法等内容,帮助开发者深入理解并高效使用Objective-C。

RunTime 一个貌似很高深的东西,但是如果真的会用了其实也不是很难

情况1 在程序运行的时候更换已经写好的方法

事例代码
//
//  Studet.m
//  Runtime_Nscoding
//
//  Created by 杨小兵 on 15/8/5.
//  Copyright (c) 2015年 杨小兵. All rights reserved.
//

#import "Studet.h"
#import <objc/runtime.h>

@implementation Studet
+ (void)load
{
    // 获取类方法
    Method m1 = class_getClassMethod([self class], @selector(test1:));
    Method m2 = class_getClassMethod([self class], @selector(test2:));
    method_exchangeImplementations(m1, m2);
    //获取对象方法
    Method m3 = class_getInstanceMethod([self class], @selector(test1:));
    Method m4 = class_getInstanceMethod([self class], @selector(test2:));
    method_exchangeImplementations(m3, m4);
}
- (instancetype)init
{
    if (self = [super init])
    {
        [self test1:@"test1"];
        [self test2:@"test2"];
    }
    return self;
}
+ (void)test1:(NSString *)string
{
    
    NSLog(@"我是+++++test1 %@ %@",NSStringFromSelector(_cmd),string);
}
+ (void)test2:(NSString *)string
{
    NSLog(@"我是-----test2 %@ %@",NSStringFromSelector(_cmd),string);
}

- (void)test1:(NSString *)string
{
    
    NSLog(@"我是+++++test1 %@ %@",NSStringFromSelector(_cmd),string);
}
- (void)test2:(NSString *)string
{
    NSLog(@"我是-----test2 %@ %@",NSStringFromSelector(_cmd),string);
}
- (NSString *)description
{
    NSMutableString *string = [NSMutableString stringWithFormat:@"<%@: %p>\n{",[self class],self];
    Class c = self.class;
    // 截取类和父类的成员变量
    while (c && c != [NSObject class])
    {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList(c, &count);
        for (int i = 0; i < count; i++)
        {
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
            [string appendFormat:@"\n%@:%@ ",key,[self valueForKeyPath:key]];
        }
        // 获得c的父类
        c = [c superclass];
        free(ivars);
    }
    [string appendFormat:@"\n}"];
    return string;
}
@end



对应的输出结果:
2015-08-05 13:35:29.565 Runtime_Nscoding[5343:161381] 我是-----test2 test1: test1
2015-08-05 13:35:29.566 Runtime_Nscoding[5343:161381] 我是+++++test1 test2: test2

情况2 通过分类(类目)给类动态的添加属性

我们知道catalogue可以给一个类添加方法,但是添加属性的话默认的情况下是不可以的。但是我们可以通过运行时的方法给一个类添加属性示例代码如下:
Person+More.h
//
//  Person+More.h
//  Runtime_Nscoding
//
//  Created by 杨小兵 on 15/8/5.
//  Copyright (c) 2015年 杨小兵. All rights reserved.
//

#import "Person.h"
/**
 *  动态的添加属性
 */
@interface Person (More)

/**
 *  为每一个对象添加一个name属性
 */
@property (nonatomic,copy) NSString *bookName;
/**
 *  为每个对象添加一个数组属性
 */
@property (nonatomic,strong) NSArray *books;
@end

//
//  Person+More.m
//  Runtime_Nscoding
//
//  Created by 杨小兵 on 15/8/5.
//  Copyright (c) 2015年 杨小兵. All rights reserved.
//

#import "Person+More.h"
#import <objc/runtime.h>

@implementation Person (More)
// 用一个字节来存储key值,设置为静态私有变量,避免外界修改
static char bookNameKey;
- (void)setBookName:(NSString *)bookName
{
    
    // 将某个值与某个对象关联起来,将某个值存储到某个对象中
        objc_setAssociatedObject(self, &bookNameKey, bookName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)bookName
{
    return objc_getAssociatedObject(self, &bookNameKey);
}

static char booksKey;
- (void)setBooks:(NSArray *)books
{
    objc_setAssociatedObject(self, &booksKey, books, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSArray *)books
{
    return objc_getAssociatedObject(self, &booksKey);
}

@end

这样的话就活增加两个属性
    Person *person = [[Person alloc] init];
    person.bookName = @"book";
    person.books = @[@"book1",@"book2"];
    NSLog(@"%@,%@",person.bookName,person.books);

2015-08-05 14:18:35.930 Runtime_Nscoding[5736:192376] *******<Studet: 0x7fda6b70eba0>
{
_studyAge:一年级 
_name:name 
_age:12 
}



情况3 利用运行时实现NSCoding协议

我们可以再程序运行的时候动态获取对象的方法列表和属性列表,这样的话我们就可以利用属性列表动态的实现NSCoding协议,而不用每个属性都自己去写具体的实现代码如下:
//
//  Person.m
//  Runtime_Nscoding
//
//  Created by 杨小兵 on 15/8/5.
//  Copyright (c) 2015年 杨小兵. All rights reserved.
//

#import "Person.h"
#import <objc/runtime.h>

@implementation Person
- (id)initWithCoder:(NSCoder *)aDecoder
{
    
    if (self = [super init]) {
        Class c = self.class;
        // 截取类和父类的成员变量
        while (c && c != [NSObject class]) {
            unsigned int count = 0;
            Ivar *ivars = class_copyIvarList(c, &count);
            for (int i = 0; i < count; i++) {
                
                NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
                
                id value = [aDecoder decodeObjectForKey:key];
                
                [self setValue:value forKey:key];
                
            }
            // 获得c的父类
            c = [c superclass];
            free(ivars);
        }
        
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    
    
    Class c = self.class;
    // 截取类和父类的成员变量
    while (c && c != [NSObject class]) {
        unsigned int count = 0;
        
        Ivar *ivars = class_copyIvarList(c, &count);
        
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            id value = [self valueForKey:key];
            
            [aCoder encodeObject:value forKey:key];
        }
        c = [c superclass];
        // 释放内存
        free(ivars);
    }
}
@end


然后我们又写了一个类 Student继承自Person
//
//  Studet.h
//  Runtime_Nscoding
//
//  Created by 杨小兵 on 15/8/5.
//  Copyright (c) 2015年 杨小兵. All rights reserved.
//

#import "Person.h"

@interface Studet : Person
/**
 *  年级
 */
@property(nonatomic , copy)NSString *studyAge;
- (void)test1:(NSString *)string;
- (void)test2:(NSString *)string;
@end
Person的.m文件的源码在情况一里边已经贴出来了,可以看出来里边没有实现NScoding,所以应该是调用Person的相关方法,程序依然可以完美的运行
Studet *stu = [[Studet alloc]init];
    stu.name = @"name";
    stu.age = 12;
    stu.studyAge = @"一年级";
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    //将对象归档
    path = [NSString stringWithFormat:@"%@/stu.data",path];
    [NSKeyedArchiver archiveRootObject:stu toFile:path];
    //将对象解档
    Studet *stu2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    NSLog(@"%@",stu2);

输出结果如下:
2015-08-05 14:18:35.930 Runtime_Nscoding[5736:192376] *******<Studet: 0x7fda6b70eba0>
{
_studyAge:一年级 
_name:name 
_age:12 
}

情况4动态的提那家方法

我这里仅动态的添加了一个有参函数和无参函数其代码实现如下
#pragma mark- 动态的添加了一个方法

/**
 *  动态的添加的方法
 *
 */
void  newFunction1(__strong id self, SEL _cmd){
    NSLog(@"%@  是动态添加的方法", [self name]);
}
void  newFunction2(__strong id self, SEL _cmd ,NSString *conten){
    NSLog(@"%@  是动态添加的方法,参数是 %@", [self name],conten);
}
//动态添加方法:在resolve中添加相应的方法,注意是类方法还是对象方法。
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if ([NSStringFromSelector(sel) isEqualToString:@"newFunction1"])
    {
        class_addMethod(self, sel, (IMP)newFunction1, "v@:"); // 为sel指定实现为newFunction1
        return YES;
    }
    if ([NSStringFromSelector(sel) isEqualToString:@"newFunction2:"])
    {
        class_addMethod(self, sel, (IMP)newFunction2, "v@:@"); // 为sel指定实现为newFunction2
        return YES;
    }
    return YES;
}


#pragma mark - 类方法
void  newFunction(__strong id self, SEL _cmd){
    NSLog(@"%@  是动态添加的方法#########################", [self class]);
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
    if ([NSStringFromSelector(sel) isEqualToString:@"newFunction"])
    {
        class_addMethod(self, sel, (IMP)newFunction, "v@:"); // 为sel指定实现为newFunction
        return YES;
    }
    return YES;
}

调用的代码如下
- (void)test3
{
    
//    [Student  methodForSelector:("newFunction")];
    Student * student = [[Student alloc] init];
    
    student.name = @"test3";
    
    [student performSelector:@selector(newFunction1)];
    [student performSelector:@selector(newFunction2:) withObject:@"world"];
    
}

其输出效果如下
2015-08-05 16:45:42.285 Runtime_Nscoding[9980:303931] 我是-----test2 test1: test1
2015-08-05 16:45:42.285 Runtime_Nscoding[9980:303931] 我是+++++test1 test2: test2
2015-08-05 16:45:42.286 Runtime_Nscoding[9980:303931] test3  是动态添加的方法
2015-08-05 16:45:42.286 Runtime_Nscoding[9980:303931] test3  是动态添加的方法,参数是 world
现在有一个缺点就是类方法知道如何添加但是不知道如何调用

本博文的实例代码链接  https://github.com/sixTiger/Runtime_Nscoding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值