您的位置:首页 > 其它

runtime

2015-10-03 15:01 465 查看


runtime主要就是做一些底层的操作,如:
1. 动态的添加对象的成员变量和方法
2.动态交换两个方法的实现(可以替换系统的方法)
3.获得某个类的所有成员方法、所有成员变量
4. 实现分类也可以添加属性
5.实现NSCoding的自动归档和解档
6.实现字典转模型的自动转换


替换系统方法,可以通过拦截系统的方法探究底层,比如block 的实现原理


常用方法

1.获取类中的方法
Method class_getClassMethod(Method cls , SEL name)


如:
Method m = class_getClassMethod([Person class],@selector(setName:));


2.获取对象中的方法
Method class_getInstanceMethod(Method cls, SEL name)


如:
Person *person = [[Person alloc] init];
Method m = get_InstanceMethod([person class],@selector(setName:));


3.交换两个方法的实现
void method_exchangeImplementations(Method m1,Method m2)



Person *p =[[Person alloc] init];
[p study];
[p run];
//交换实现
//instance method :实例方法,
//class_getInstanceMethod得到实例的方法(即对象方法)
//两个参数 1:类名 2.方法名
//class_getClassMethod :得到实例化的方法
Method m1 = class_getInstanceMethod([Person class], @selector(study));
Method m2 = class_getInstanceMethod([Person class], @selector(run));
method_exchangeImplementations(m2, m1);
[p study];
[p run];


具体操作



051B23EE-AFC6-4C95-9297-1E58708D5B96.png

4.获取成员变量
Ivar  *ivars = class_getCopyIvarList(Ivar ivar);


实现分类中添加属性

为所有的NSObject对象添加属性

1.首先创建一个NSObject分类NSObject+Extension

2.在.h中使用@property添加属性
此时使用@property添加数属性,并非真正的属性,如果此时调用查看属性,将会崩溃,
因为分类并未实现添加添加属性的功能,想要添加属性,需要使用runtime,动态的添加


3.在.m文件中实现getter和setter方法
如果想要添加多个属性,就需要在每个对象中抽出一块空间用于存放属性,
使用objc_setAssociatedObject方法进行关联

#import "NSObject+Extension.h"
#import <objc/runtime.h>
@implementation NSObject (Extension)
//用于存放属性的变量,多个属性,需要创建不同的变量
char BookKey;
-(void)setBooks:(NSArray *)books{
objc_setAssociatedObject(self, &BookKey, books, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSArray *)books{
return objc_getAssociatedObject(self, &BookKey);
}
@end


遵守协议NSCoding,实现属性的自动归档与解档

需求分析:

当想要对象自动进行归档解档的时候,如果属性非常的多,一个一个天添加[encoder encodeObject:@(xxx) forKey:@"_xxx"];将会非常的繁琐。

既然能够获取所有的属性,我们就可以通过循环遍历属性的方式进行统一的归档和解档
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
// 用来存储成员变量的数量
unsigned int outCount = 0;

// 获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);

// 遍历所有的成员变量
for (int i = 0; i<outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];

NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 获得key对应的值
id value = [decoder decodeObjectForKey:key];

// 设置到成员变量上
[self setValue:value forKeyPath:key];
}

free(ivars);
}
return self;
}

/**
* 将对象写入文件时会调用这个方法(开发者需要在这个方法中说明需要存储哪些属性)
*/
- (void)encodeWithCoder:(NSCoder *)encoder
{
// 用来存储成员变量的数量
unsigned int outCount = 0;

// 获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);

// 遍历所有的成员变量
for (int i = 0; i<outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];

NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 通过key获得对应成员变量的值
id value = [self valueForKeyPath:key];

[encoder encodeObject:value forKey:key];
}

free(ivars);
}


注意:
ARC的内存管理机制 只适合OC语法,对于C语言的内存还是需要手动的释放,当使用runtime的时候,
如果包含了copy、create、retain、new等词语,那么在最后就需要释放内存

使用free(对象)进行释放如:free(ivars);


利用runtime实现字典转模型

描述:
KVC的字典转模型具有一个缺陷,就是属性的数量与名称都必须保持一致,如果字典中的属性多,
而模型中没有使用KVC赋值的时候就会崩溃,需要实现另一个方法
setValue:forUndefinedKey:方法,并如果对象中包含了另一个对象作为属性,
也将不能自动将其转化为模型

而使用runtime实现的字典转模型,可以实现将所有的对象都转化为对应的模型,
并且不会出现属性找不到,而奔溃的现象


NSObject+Extension.h
#import <Foundation/Foundation.h>
@interface NSObject (Extension)
-(void)setDiction:(NSDictionary *)dict;
+(instancetype)objectWithDiction:(NSDictionary *)dict;
@end


NSObject+Extension.m
#import "NSObject+Extension.h"
#import <objc/runtime.h>

@implementation NSObject (Extension)

-(void)setDiction:(NSDictionary *)dict{
//获取类
Class c = self.class;
//循环遍历 类 (本类 和所有的父类)
while (c && c != [NSObject class]) {
//获取所有的属性
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(c, &outCount);
//遍历类中的属性
for (int i = 0; i < outCount; i++) {
//获取属性名
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//去掉key中的 _
key = [key substringFromIndex:1];
//通过key 获取属性的值
id value = dict[key];
//如果key是一个空值 退出本轮的循环
//原因:如果字典中没有这个key,那么value将会是一个空值,kvc 不能赋值空值
if (value == nil) {
continue;
}
//如果类中包含另一个类为对象,也要将该对象进行字典转模型
//获取对象的属性的类名
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// 对象名会以@“名字”的形式 出现,但是同时字符串也是以这种形式表示,因此可以先判断type中是否包含 @ 符号
NSRange range = [type rangeOfString:@"@"];
//如果range.location 不等于NSNotFound说明 找到了@
if (range.location != NSNotFound) {
//截取type中的名字 去除@“ ”
type = [type substringWithRange:NSMakeRange(2, type.length -3)];
if (![type hasPrefix:@"NS"]) {
//将type转化为类名
Class class = NSClassFromString(type);
value = [class objectWithDiction:value];
}

}

//赋值
[self setValue:value forKey:key];

}
//ARC 只适用于OC语法,C语言中的内存 需要手动释放
free(ivars);
c = [c superclass];
NSLog(@"1");
}
}
+(instancetype)objectWithDiction:(NSDictionary *)dict{
NSObject *obj = [[self alloc] init];
[obj setDiction:dict];
return  obj;
}
@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: