第31月第17天 resolveInstanceMethod
1.
#import "UIView+Test.h" #import <objc/runtime.h> @implementation UIView (Test) + (BOOL)resolveInstanceMethod:(SEL)sel { return NO; } - (id)forwardingTargetForSelector:(SEL)aSelector { // NSString *sel = NSStringFromSelector(aSelector); { //2 Class class = objc_allocateClassPair(NSClassFromString(@"NSObject"),"AvoidCrashTarget",0); class_addMethod(class, aSelector, class_getMethodImplementation([self class], @selector(avoidCrashAction)), "@@:"); id tempObject = [[class alloc] init]; return tempObject; } return [super forwardingTargetForSelector:aSelector]; } - (NSObject *)avoidCrashAction { return nil; } @end
在Objective-C中,在调用对象的某个方法时,其实是在向这个对象发送消息。系统会查看这个对象能否接收该消息,如果不能,则会进行消息转发,消息转发最多三个步骤(注:如果前一步成功处理了消息,那么就不会走到下一步):
-
调用
resolveInstanceMethod:
或resolveClassMethod:
来决定是否动态添加方法。如果动态添加,则消息得到处理,消息转发结束;否则,进入下一步。 -
调用
forwardingTargetForSelector:
来确定能不能把这条消息转给其他接收者处理,如果返回一个非self
的对象,则会把消息发送给该对象,消息转发结束;否则,进入下一步。 -
通过
methodSignatureForSelector:
方法获取签名,如果签名为nil,则消息无法处理,抛出异常;否则,调用forwardInvocation:
方法,调用成功则消息转发结束,调用失败则消息无法处理,抛出异常。
id GetterName(id self, SEL cmd) { return objc_getAssociatedObject(self, cmd); } void SetterName(id self, SEL cmd, NSString *value) { NSString *sel = NSStringFromSelector(cmd); NSString *key = [sel substringWithRange:NSMakeRange(3, sel.length - 4)].lowercaseString; objc_setAssociatedObject(self, NSSelectorFromString(key), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } if (sel == @selector(name)) { // "@@:"的意思: // 第一个字符表示函数的返回值类型,"@"表示GetterName函数的返回值类型为id // 后面的字符表示函数的参数类型,"@:"表示GetterName函数接收两个参数, // "@"表示参数类型为id,":"表示参数类型为SEL class_addMethod(self, sel, (IMP)GetterName, "@@:"); return YES; } if (sel == @selector(setName:)) { // "v@:@"的意思: // "v"表示SetterName函数的返回值类型为void // "@:@"参见上面 class_addMethod(self, sel, (IMP)SetterName, "v@:@"); return YES; }
2.
给`NSNull`创建一个分类,并在.m中实现: #import "NSNull+safe.h" @implementation NSNull (safe) #define pLog #define JsonObjects @[@"",@0,@{},@[]] - (id)forwardingTargetForSelector:(SEL)aSelector { for (id jsonObj in JsonObjects) { if ([jsonObj respondsToSelector:aSelector]) { #ifdef pLog NSLog(@"NULL出现啦!这个对象应该是是_%@",[jsonObj class]); #endif return jsonObj; } } return [super forwardingTargetForSelector:aSelector]; }