利用runtime来实现归档解档
//告诉系统,归档哪些属性
- (void)encodeWithCoder:(NSCoder *)coder
{
//利用runtime 来归档!!
unsigned int count = 0;
/*
拷贝Person类里的所有成员变量
&count 获得count的地址 来改变它的值
& 取地址符
count Person这个类的成员变量的个数
*/
Ivar * ivars = class_copyIvarList([self class], &count);
/*
好玩的!
Ivar * ivars = class_copyIvarList([UIButton class], &count);
获取button的所有属性 然后通过kvc改变button的属性
*/
for (int i = 0; i < count; i++) {
//拿出每一个Ivar
Ivar ivar = ivars[i];
/*
ivar_getName 获取变量名称
*/
const char * name = ivar_getName(ivar);
/*
C语言的char转换成oc对象的字符串
*/
NSString * KEY = [NSString stringWithUTF8String:name];
//归档
/*
通过KVC拿到值 [self valueForKey:KEY]
*/
[coder encodeObject: [self valueForKey:KEY] forKey:KEY];
}
//C语言里面!! 一旦遇到了copy creat new 需要释放
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];
}
free(ivars);
}
return self;
}
方法交换 俗称 OC的方法欺骗
#import "NSURL+url.h"
#import <objc/message.h>
@implementation NSURL (url)
//重写!! 将系统所有的行为都改变了!!
//+(instancetype)URLWithString:(NSString *)URLString{
//// 原来系统的处理方式被我们改变了!!
// //创建url
// NSURL * url = [[NSURL alloc]initWithString:URLString];
// if (!url) {
// NSLog(@"哥么为nil!!");
// }
// return url;
//}
//
//load 方法当类被加载的时候调用
+(void)load
{
// IMP URLWithStr = class_getMethodImplementation([self class], @selector(URLWithString:));
// IMP HKURLSTR = class_getMethodImplementation([self class], @selector(HK_URLWSTR:));
//
// Method URLWithStrM = class_getClassMethod([self class], @selector(URLWithString:));
//
// method_setImplementation(URLWithStrM, HKURLSTR);
/*
class_getClassMethod 获取类方法
class_getInstanceMethod 获取对象方法
*/
Method URLWithStr = class_getClassMethod([self class], @selector(URLWithString:));
Method HKURLSTR = class_getClassMethod([self class], @selector(HK_URLWSTR:));
//交换
method_exchangeImplementations(URLWithStr, HKURLSTR);
}
//看起来就是死循环的代码!!其实不是
+(instancetype)HK_URLWSTR:(NSString *)str{
//创建URL
/*
这时候因为方法已经交换过了
[NSURL HK_URLWSTR:str]; 走到了系统的方法实现!!
[NSURL URLWithString:str]; 走到了自定义的 HK_URLWSTR 方法
*/
NSURL * url = [NSURL HK_URLWSTR:str];
if (!url) {
NSLog(@"哥么nil");
}
return nil;
}
KVO的实现原理
/**原理
Person里有一个属性age
KVO监听age属性变化
首先系统会动态的创建一个类继承自Person类
然后重写age的set方法(这些都是通过runtime来做的)
- (void)setAge:(int)age
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
然后改变p的指针,指向子类;
这时候再改变age这个属性的值得时候,他就会走子类的set方法;
willChangeValueForKey和didChangeValueForKey这两个方法会调observeValueForKeyPath;
这样就能监听到属性的变化;
*/
Person * p = [[Person alloc] init];
[p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
}
用runtime来实现KVO
objc_allocateClassPair
创建一个类
class_addMethod
添加一个方法
//监听某个对象的属性
//谁调用监听谁
-(void)HKaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
//1.动态创建一个类
//1.1 动态类名
NSString * oldName = NSStringFromClass([self class]);
NSString * newStr = [@"HankKVO_" stringByAppendingString:oldName];
const char * newName = [newStr UTF8String];
//1.2 继承的类 2.类名
Class Myclass = objc_allocateClassPair([self class], newName, 0);
//添加set方法
class_addMethod(Myclass, @selector(setName:), (IMP)setName, "v@:@");
//注册该类
objc_registerClassPair(Myclass);
//修改观察者的isa指针
object_setClass(self, Myclass);
//将观察者保存到当前对象中
objc_setAssociatedObject(self, @"hank666", observer, OBJC_ASSOCIATION_ASSIGN);
}
//函数搞定!!
void setName(id self,SEL _cmd,NSString * newName){
//调用super
//1.保存当前类型
Class class = [self class];
//改变
object_setClass(self, class_getSuperclass(class));
objc_msgSend(self, @selector(setName:),newName);//相当于调用 super setName
object_setClass(self, class);
//拿到观察者
id obsever = objc_getAssociatedObject(self, @"hank666");
objc_msgSend(obsever, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,nil,nil);
}
网友评论