前面我们说到:
Runtime 消息传递机制
Runtime 消息转发机制
Runtime 交换方法
今天我们来谈谈Runtime的一些常用的用法
1.KVO的底层实现
KVO:Key-Value-Observe 键值监听,动态监听属性的变化
具体用法
//这里以监听Person的age属性为例
self.person = [Person new];
self.person.age = 21;
//此行注册监听
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
然后实现如下方法,当每次age改变,都会调用下面的方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"%@监听到%@属性的改变为%@",object,keyPath,change);
}
那么KVO是怎么实现的呢?
1、自定义当前类的子类
2、重写set方法,在内部恢复父类的做法,通知观察者
3、通过修改当前类对象的isa指针,指向自定义的子类来让外界调用自定义类的方法
//此行注册监听后,利用Runtime在程序运行过程中,改变了这个对象的类型:NSKVONotifing_Person
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
而生成的NSKVONotifing_Person类中会重写age的set方法
/** 重写set方法 */
-(void)setAge:(int)age{
[super setAge:age];
//这两个方法会默认调用观察者的observeValueForKeyPath方法
[self willChangeValueForKey:@"age"];
[self didChangeValueForKey:@"age"];
}
原来是在重写的set方法里面做了手脚。
如下是没有设置监听age属性时,对Person的断点调试
如下是设置监听age属性后,对Person的断点调试
2.获取对象的所有(包括私有)属性、变量、方法
2.1获取对象的所有属性
- (IBAction)allProperty:(id)sender {
// 定义变量
unsigned int count;
// 运行时获取私有属性列表
objc_property_t *properties = class_copyPropertyList([Person class], &count);
for (int index = 0; index < count; ++index) {
// 声明属性
objc_property_t property = properties[index];
// 指针数组. 通过property_getName获取属性的名称
const char *cName = property_getName(property);
// UTF8转码
NSString *name = [NSString stringWithUTF8String:cName];
NSLog(@"name -----> %@", name);
}
free(properties);
}
2.2获取对象的所有属性+变量
- (IBAction)allIvar:(id)sender {
unsigned int count = 0;
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 * KEY = [NSString stringWithUTF8String:name];
NSLog(@"name -----> %@", KEY);
}
//c语言里面需要释放指针!!
free(ivars);
}
2.3获取对象的所有方法
- (IBAction)allMethod:(UIButton *)sender {
// 定义变量
unsigned int count;
// 运行时获取私有方法列表
Method *methods = class_copyMethodList([Person class], &count);
for (int index = 0; index < count; ++index) {
Method method = methods[index];
// 这步完了之后, 可以获取到方法的地址
SEL selector = method_getName(method);
NSString *name = NSStringFromSelector(selector);
// 确定方法类型
const char *type = method_getTypeEncoding(method);
NSLog(@"fun-----%@, type-----> %s",name,type);
}
free(methods);
}
3.序列化与反序列化
还记得当初代码里面关于序列化与反序列化的代码一堆一堆的
如上是属性比较少的,我看到过多的有上百行的,简直要翻好几页才能看完,更何况是写呢?
你是不是也想过简化这段代码,现在,可以用runtime来简化
#import "CodingModel.h"
#import
@implementation CodingModel
- (void)encodeWithCoder:(NSCoder *)coder
{
unsigned int count = 0;
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];
//取出属性的值
id value = [self valueForKey:KEY];
//归档
[coder encodeObject:value forKey:KEY];
}
//c语言里面需要释放指针!!
free(ivars);
}
//解档!!
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
unsigned int count = 0;
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];
//解档
id value = [coder decodeObjectForKey:KEY];
//将值设置到属性上 KVC
[self setValue:value forKey:KEY];
}
//c语言里面需要释放指针!!
free(ivars);
}
return self;
}
@end
哈哈,总共20行,搞定
你可以写个基类,在需要用到序列化的Model里直接继承这个基类。搞定
文章就到这里
提供一份Demo,希望对你有帮助
想了解更多RunTime,可以参考:
Runtime 消息传递机制
Runtime 消息转发机制
Runtime 交换方法
写在最后:
希望这篇文章对您有帮助。当然如果您发现有可以优化的地方,希望您能慷慨的提出来。最后祝您工作愉快!