美文网首页
Runtime学习笔记

Runtime学习笔记

作者: LD_左岸 | 来源:发表于2017-12-30 09:13 被阅读23次

    一.消息机制

    OC调用方法是动态调用 调用未实现的方法编译不报错 方法调用的本质是发送消息

    import <objc/message.h>
    [p eat];
    [p performSelector:@selector(eat)];
    
    objc_msgSend();
    Xcode5之后使用运行时可设置
    objc_msgSend(p, @selector(eat:),@"🎃");//传递参数
    
    
    F36AF365-C511-49F8-B00E-AE53EF06A8E9.png

    方法调用的本质是 执行performSelector 把一个方法名 转化成一个方法编号 根据这个方法编号去方法列表中查找对应的方法 调用方法的实现

    二.交换方法

    参考https://www.jianshu.com/p/9e3cf04f3dc8

    #import <Foundation/Foundation.h>
    @interface NSArray (ldd)
    
    @end
    
    #import "NSArray+ldd.h"
    #import <objc/message.h>
    @implementation NSArray (ldd)
    //const char * const LD_Key = "Key";
    + (void)load
    {
        [super load];
        
        Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
        Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(ldd_objectAtIndex:));
        method_exchangeImplementations(fromMethod, toMethod);
        
        Method fromMutMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(objectAtIndex:));
        Method toMutMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(ldd_mutaObjectAtIndex:));
        method_exchangeImplementations(fromMutMethod, toMutMethod);
    }
    - (id)ldd_objectAtIndex:(NSUInteger)index
    {
        if (index < self.count) {
            return [self ldd_objectAtIndex:index];
        }else{
            return nil;
        }
    }
    - (id)ldd_mutaObjectAtIndex:(NSUInteger)index
    {
        if (index < self.count) {
            return [self ldd_mutaObjectAtIndex:index];
        }else{
            return nil;
        }
    }
    @end
    

    三.动态添加方法

    Person * p = [[Person alloc]init];
     //[p eat]; //编译不过
    //动态添加方法
     [p performSelector:@selector(eat) withObject:nil];//编译的过 运行崩
    
    //
    //  Person.m
    //  test
    //
    //  Created by 李洞洞 on 27/12/17.
    //  Copyright © 2017年 Minte. All rights reserved.
    //
    
    #import "Person.h"
    #import <objc/message.h>
    @implementation Person
    void ldd(id ldself,SEL ldsel){
        NSLog(@"这是一个动态添加的方法");
        NSLog(@"%@ -- %@",[ldself class],NSStringFromSelector(ldsel));
    };
    //调用时刻:当在外界调用了当前类未实现的方法时调用
    + (BOOL)resolveClassMethod:(SEL)sel
    {
        return [super resolveClassMethod:sel];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == NSSelectorFromString(@"eat")) {
    //IMP:方法的实现 函数的入口 函数名 函数指针
    //types:方法的类型
            class_addMethod([self class], @selector(eat), (IMP)ldd, "V@:");
        }
        return [super resolveInstanceMethod:sel];
    }
    @end
    
    1385290-b085510875abb98d.png
    #pragma mark -- 动态添加方法
        Person * p = [[Person alloc]init];
        [p performSelector:@selector(eat:) withObject:@"🎃"];
    
    #import "Person.h"
    #import <objc/message.h>
    @implementation Person
    void ldd(id ldself,SEL ldsel,id some){
        NSLog(@"这是一个动态添加的方法");
        NSLog(@"%@ -- %@",[ldself class],NSStringFromSelector(ldsel));
    };
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == NSSelectorFromString(@"eat:")) {
            class_addMethod([self class], @selector(eat:), (IMP)ldd, "V@:@");
        }
        return [super resolveClassMethod:sel];
    }
    

    四.动态添加属性

    #import <UIKit/UIKit.h>
    
    @interface UIButton (ldd)
    /*
     不生成_ld_Title
     不生成 ld_Title的set get
     */
    @property NSString * ld_Title;
    @end
    
    #import "UIButton+ldd.h"
    #import <objc/message.h>
    @implementation UIButton (ldd)
    const char * const LD_Key = "Key";
    - (void)setLd_Title:(NSString *)ld_Title
    {
        objc_setAssociatedObject(self, LD_Key, ld_Title, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    - (NSString*)ld_Title
    {
    
        return objc_getAssociatedObject(self, LD_Key);
    }
    @end
    

    如果不用以上的写法 手动实现set get方法也可以 不好的地方是:
    静态全局变量来保存值 如果我这个对象销毁了 这个变量保存的值还在 此时你变量保存的值和对象之间并没任何关系 和我这个对象无关了。 最好的状态是我这个对象在 这个属性在 对象不在 属性也不在。

     #pragma mark -- 动态添加属性
     UIButton * btn = [[UIButton alloc]init];
     btn.ld_Title = @"ldd";
     NSLog(@"%@",btn.ld_Title);
     btn.ld_Title = @"999";
     NSLog(@"%@",btn.ld_Title);
    

    或者这么玩

    static char * const LD_Key = "Key";
    static char * const LD_ButtonKey = "ButtonKey";
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIButton * button = ({
            UIButton * btn = [[UIButton alloc]init];
            objc_setAssociatedObject(btn, LD_ButtonKey, @"6666", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            btn;
        });
        [self.view addSubview:button];
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",objc_getAssociatedObject(button, LD_ButtonKey));
        });
    
        
        
        UIView * view1 = ({
            UIView * view = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
            view.backgroundColor = [UIColor redColor];
            UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(view1Click:)];
            NSDictionary * value= @{@"color":[UIColor greenColor]};
            objc_setAssociatedObject(view, LD_Key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            [view addGestureRecognizer:tap];
            view;
        });
        [self.view addSubview:view1];
    }
    - (void)view1Click:(UITapGestureRecognizer*)tap
    {
        UIView * view = tap.view;
        NSDictionary *value = objc_getAssociatedObject(view, LD_Key);
        view.backgroundColor = value[@"color"];
    }
    @end
    

    五.字典转模型

    #import <Foundation/Foundation.h>
    @protocol LDModel <NSObject>
    @optional
    
    /**
     模型字段是数组
    
     @return {模型字段 : 数组中存储的自定义模型的类名}
     */
    + (NSDictionary *)objectClassInArray;
    
    /**
     模型中特殊字段(id...)的替换
    
     @return {模型中的字段:字典中的特殊字段}
     */
    + (NSDictionary *)propertykeyReplacedWithValue;
    @end
    
    @interface NSObject (Prepory)<LDModel>
    + (id)modelWithDict:(NSDictionary*)dict;
    @end
    
    #import "NSObject+Prepory.h"
    #import <objc/message.h>
    @implementation NSObject (Prepory)
    + (id)modelWithDict:(NSDictionary *)dict
    {
        id object = [[self alloc]init];
        unsigned int count = 0;
        Ivar * ivarList = class_copyIvarList([self class], &count);
        for (int i = 0; i < count; i++) {
            Ivar  ivar = ivarList[i];
            char const *  name = ivar_getName(ivar);
            NSString * ocName = [NSString stringWithUTF8String:name];
            NSString * key = [ocName substringFromIndex:1];
            id value = nil;
            if ([self respondsToSelector:@selector(propertykeyReplacedWithValue)]) {
                key = [[self propertykeyReplacedWithValue]objectForKey:key];
                
                if (!key) {
                    key = [ocName substringFromIndex:1];
                }
    
                value = dict[key];
                
            }
             key = [ocName substringFromIndex:1];
            
            const char * name1 = ivar_getTypeEncoding(ivar);
            NSString * newName = [[NSString alloc]initWithUTF8String:name1];
            
            
            if ([value isKindOfClass:[NSDictionary class]]&&![key hasPrefix:@"NS"]) {
                newName = [newName stringByReplacingOccurrencesOfString:@"\"" withString:@""];
                newName = [newName stringByReplacingOccurrencesOfString:@"@" withString:@""];
                Class class = NSClassFromString(newName);
                id val = [class modelWithDict:value];
                [object setValue:val forKey:key];
                
                }else if ([value isKindOfClass:[NSArray class]]&&![key hasPrefix:@"NS"]){
                    
                    if ([self respondsToSelector:@selector(objectClassInArray)]) {
                        id propertyValueType = [[self objectClassInArray] objectForKey:key];
                        Class clas = NSClassFromString(propertyValueType);
                        NSArray * arr = (NSArray*)value;
                        id obj = nil;
                        NSMutableArray * muArr = [NSMutableArray array];
                        for (int i = 0; i < arr.count; i++) {
                           obj = [clas modelWithDict:arr[i]];//key里面放的是区县模型//这是一个模型
                            [muArr addObject:obj];
                        }
                        [object setValue:muArr forKey:key];
    
                    }
                    
                    
                }else if (value) {
    
                    
                    [object setValue:value forKey:key];
                    
                }
                   
        }
        free(ivarList);
        return object;
    }
    @end
    
    

    六.KVO的底层实现

    当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的setter方法。派生类在被重写的setter方法内实现真正的通知机制
    如果原类为Person,那么生成的派生类名为NSKVONotifying_Person。

    每一个类中都有一个isa指针指向当前类,所有系统就是在当一个类的对象第一次被观察的时候,系统就会将isa指针指向动态生成的派生类,从而在被监听属性赋值时被执行的是派生类的setter方法

    键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey: ofObject: change: context: 也会被调用。

    七.归档&解档

    #import <Foundation/Foundation.h>
    @interface Person : NSObject<NSCoding>
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,copy) NSString *age;
    @property (nonatomic,copy) NSString *height;
    @end
    
    #import "Person.h"
    #import <objc/message.h>
    @implementation Person
    - (void)encodeWithCoder:(NSCoder *)aCoder
    {
        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];
            //归档
            [aCoder encodeObject:value forKey:key];
        }
    }
    - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        if (self = [super init]) {
            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 = [aDecoder decodeObjectForKey:key];
                [self setValue:value forKey:key];
            }
        }
        return self;
    }
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        Person * p = [[Person alloc]init];
        p.name = @"ldd";
        p.age = @"26";
        p.height = @"1.80";
        NSString * filePath = [@"/Users/fangduozhang/Desktop" stringByAppendingPathComponent:@"LDD.data"];
        [NSKeyedArchiver archiveRootObject:p toFile:filePath];
    }
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        Person * p = [NSKeyedUnarchiver unarchiveObjectWithFile:[@"/Users/fangduozhang/Desktop" stringByAppendingPathComponent:@"LDD.data"]];
        NSLog(@"%@---%@---%@",p.name,p.age,p.height);
    }
    

    八.数据库动态方案

    https://github.com/Buliceli/LDnamicDataBase

    鉴于能力有限 水平一般 理解有误之处 望大侠不吝指出 ThankYou!

    相关文章

      网友评论

          本文标题:Runtime学习笔记

          本文链接:https://www.haomeiwen.com/subject/qpywgxtx.html