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