Runtime
2020-05-26 本文已影响0人
Zorin
定义
1. 消息转发
- 动态方法解析(方法调用的必经之路)
+ (BOOL)resolveClassMethod:(SEL)sel {
// 获取方法名称
NSString * selName = [NSString stringWithFormat:@"%@",NSStringFromSelector(sel)];
// 当识别到 某个方法时 可以动态的添加进来
if ([selName isEqualToString:@"testMethod"]) {
//为该类动态添加一个方法testFunction处理消息,并执行该方法
class_addMethod([self class], sel, (IMP)testFunction, "v@:");
return true;
}
return false;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
}
- 快速转发(动态解析失败后,指定一个接收方法的对象)
/// 快速转发
- (id) forwoardingTargetForSelector:(SEL)aSelector {
NSString * methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"])
{
return [CustomClass new];
}
return [super forwordingTargetForSelector:aSelector];
}
- 完整的消息转发(1. 消息签名 2. 转发请求)
#pragma mark 慢速转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSString * methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
};
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
CustomClass * obj = [CustomClass new];
if ([obj respondsToSelector:sel]) {
[anInvocation invokeWithTarget:obj];
} else {
[super forwardInvocation:anInvocation];
}
}
- 未识别到方法
- (void)doesNotRecognizeSelector:(SEL)aSelector {
/// 未识别到方法的调用
}
2. Method Swizzling(方法转换)
+ (void)load {
/// 对 原方法 和 新方法 进行调换
Method originMethod = class_getInstanceMethod(self,@selector(reloadData));
Method currentMethod = class_getInstanceMethod(self,@selector(lg_reloadData));
method_exchangeImplementations(originMethod,currentMethod);
}
/*
当调用 reload 的时候 就会自动调用 这个方法了
*/
- (void)lg_reloadData {
[self lg_reloadData];
[self fillDefaultView];
}
/*
当数据源没有数据的话,添加默认数据
*/
- (void)fillDefaultView {
}
3. 字典和模型的相互转换
/// 字典转模型
//1. 遍历字典的allKeys
//2. 在for循环中,动态调用model 的 (key值前拼接set) 方法名,对model 的属性进行set赋值
- (instancetype)initWithDic:(NSDictionary *)dic {
self = [super init];
if (self) {
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);
}
}
}
}
/// 模型转字典
//1. 遍历模型的属性名称( class_getPropertyList(),获取属性列表 )
//2. 遍历过程中获取,属性value 值,赋值给字典
- (NSDictionary *)convertModelToDic {
unsigned int count = 0;
objc_property_t * properties = class_copyPropertyList(self,&count);
if (count != 0) {
NSMutalbeDictionary * tempDic = [@{ } mutableCopy];
for (int i = 0; i < count; i ++ ) {
// 属性名称
const void * propertyName = property_getName(propertied[i]);
// 根据属性名称 得到获取属性value 的get方法
SEL sel = NSSelectorFromString(name);
if (sel) {
id value = ((id(*)(id, SEL, id )) objc_msgSend) (self, sel);
if (value) {
tempDic[name] = value;
} else {
temDic[name] = @"";
}
}
}
free(properties); //被copy到了堆区 要释放空间
return tempDic;
}
free(properties);
return tempDic;
}
4. 自定义KVO
- 当一个对象的方法被监听时,会自动生成一个该对象父类的子类,
- 并且该对象指向这个新类,也就是多了一个中间类,这个中间类的作用就是重写,被监听属性的set方法,并发送被修改的通知
- (vod)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
//1. 创建中间类
Class customClass = objc_allocateClassPair([self class],newName.UTF8String, 0);
objc_registerClassPair(customClass);
// 2. 修改self的isa指针,指向 中间类
object_setClass(self, customClass);
// 3. 重写 中间类的setter 方法 被通知 被通知者 值被改变
NSString * methodName = [NSString stringWithFormat:@"set%@:",keyPath.capitalizedString];
SEL sel = NSSelectorFromString(methodName);
class_addMethod(customClass, sel, (IMP)setterMethod, "v@:@");
objc_setAssociatedObject(self, (__bridge const void *)@"objc",observer, OBJC_ASSOCIATION_ASSIGN);
}
void setterMethod(id self,SEL _cmd,NSString *name) {
// 1、调用父类方法
// 2、通知观察者
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 = @""; // keyPath 方法名 去除set 后 首字母小写
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),key,self,@{key:name},nil);
}
// 引用类型 和 值类型 ??/