字典转模型
字典转模型
遍历字典获取 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