ios-Runtime(运行时)

2017-08-09  本文已影响0人  瑞阳gg
利用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);
    
}
上一篇下一篇

猜你喜欢

热点阅读