【iOS/runtime/003】字典转模型、模型转字典和自定义 KVO

字典转模型

字典转模型

遍历字典获取 key value
objc_msgSend() 调用 set 赋值
函数指针写法:返回类型 (*名称)(params1, params2…)

#import <objc/message.h>
@implementation User
- (instancetype)initWithDic:(NSDictionary *)dic {
    if (self = [super init]) {
        for (NSString* key in dic.allKeys) {
            id value = dic[key];
            NSString *methodName = [NSString stringWithFormat:@"set%@:", key.capitalizedString];
            SEL sel =  NSSelectorFromString(methodName);
            if (sel) {
                ((void(*)(id, SEL, id))objc_msgSend)(self, sel, value);
            }
        }
    }
    return self;
}
@end

模型转字典

模型转字典

class_copyPropertyList property_getName
(id)(*)(id, SEL)

#import <objc/message.h>
@implementation User
- (NSDictionary *)converModelToDic {
    unsigned int count = 0;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    if (count != 0) {
        NSMutableDictionary *tempDic = [@{} mutableCopy];
        for (int i = 0; i < count; i++) {
            const void *propertyName = property_getName(properties[i]);
            NSString *name = [NSString stringWithUTF8String:propertyName];
            SEL sel = NSSelectorFromString(name);
            if (sel) {
                id value = ((id(*)(id, SEL))objc_msgSend)(self, sel);
                if (value) {
                    tempDic[name] = value;
                }
            }
        }
    }
    free(properties);
    return nil;
}
@end

主要实现代码(自定义 KVO)

自定义 KVO

KVO 是基于 Runtime 的
A 监听 B,系统会创建子类 (NSKVONotifying_B)
B 的 isa 指针指向 NSKVONotifying_B
重写 set 方法 (验证方法:object_getClass() 获取 isa 指针)
打印地址 【_p1 methodForSelector:@selector…】

// Personal.h
#import <Foundation/Foundation.h>
#import <objc/message.h>

// 字典模型互转
@interface Personal : NSObject

@property(strong, nonatomic)NSString *nickname;

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

@end
// Personal.m
#import <objc/message.h>
@implementation Personal

void settterMethod(id self, SEL _cmd, NSString *name) {
//    1. 调用父类方法
//    2. 通知观察者调用 observer... 方法
    struct objc_super superClass = {
        self,
        class_getSuperclass([self class])
    };
//    调用父类方法
    objc_msgSendSuper(&superClass, _cmd, name);
    id observer = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
//    通知改变
    NSString *methodName = NSStringFromSelector(_cmd);
    NSString *key = getValueKey(methodName);
    objc_msgSend(observer, @selector(observeValueForKeyPath: ofObject: change: context:), key, self, @{key: name}, nil);
}

NSString *getValueKey(NSString *setter) {
    NSRange rang = NSMakeRange(3, setter.length - 4);
    NSString *key = [setter substringWithRange:rang];
    NSString *letter = [[key substringToIndex:1] lowercaseString];
    key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:letter];
    return key;
}

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    NSString *oldName = NSStringFromClass([self class]);
    NSString *newName = [NSString stringWithFormat:@"CustomKVO_%@", oldName];
//    创建自定义类,集成当前类
    Class customClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
//    注册类
    objc_registerClassPair(customClass);
//    修改 isa 指针
    object_setClass(self, customClass);
//    重写 set 方法
    NSString *methoedName = [NSString stringWithFormat:@"set%@", keyPath.capitalizedString];
    SEL sel = NSSelectorFromString(methoedName);
    class_addMethod(customClass, sel, (IMP)settterMethod, "v@:@");
//    通知观察者
    objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
}

- (void)setNickname:(NSString *)nickname {
    _nickname = nickname;
}

@end

注意

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值