Runtime学习笔记

一.消息机制

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

import 
[p eat];
[p performSelector:@selector(eat)];
objc_msgSend();
Xcode5之后使用运行时可设置
objc_msgSend(p, @selector(eat:),@"");//传递参数

Runtime学习笔记_第1张图片
F36AF365-C511-49F8-B00E-AE53EF06A8E9.png

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

二.交换方法

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

#import 
@interface NSArray (ldd)

@end
#import "NSArray+ldd.h"
#import 
@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 
@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
Runtime学习笔记_第2张图片
1385290-b085510875abb98d.png

以上图中可以通过

NSLog(@"%s",@encode(id));

来获取
或者用

Method method = class_getInstanceMethod(self,@selector(other));
method_getTypeEncoding(method);
#pragma mark -- 动态添加方法
    Person * p = [[Person alloc]init];
    [p performSelector:@selector(eat:) withObject:@""];
#import "Person.h"
#import 
@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 

@interface UIButton (ldd)
/*
 不生成_ld_Title
 不生成 ld_Title的set get
 */
@property NSString * ld_Title;
@end
#import "UIButton+ldd.h"
#import 
@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 
@protocol LDModel 
@optional

/**
 模型字段是数组

 @return {模型字段 : 数组中存储的自定义模型的类名}
 */
+ (NSDictionary *)objectClassInArray;

/**
 模型中特殊字段(id...)的替换

 @return {模型中的字段:字典中的特殊字段}
 */
+ (NSDictionary *)propertykeyReplacedWithValue;
@end

@interface NSObject (Prepory)
+ (id)modelWithDict:(NSDictionary*)dict;
@end
#import "NSObject+Prepory.h"
#import 
@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 
@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *height;
@end
#import "Person.h"
#import 
@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 *)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学习笔记)