美文网首页iOS点点滴滴码无界IOS And AndroidiOS
iOS高级开发 RunTime机制讲解五---runtime原理

iOS高级开发 RunTime机制讲解五---runtime原理

作者: kevinLY | 来源:发表于2016-03-26 19:00 被阅读219次

下载DEMO:https://github.com/sleepsheep/RunTimeDemo05
1、首先创建一个项目;
2、创建一个父类:Animal

@interface Animal : NSObject

- (void)say;

@end

@implementation Animal

- (void)say {
    NSLog(@"Animal say!");
}

@end

3、创建两个子类:Dog、DogOther

Dog:

消息的转发流程:

  • 1、(动态添加方法)接收到未知消息时:(main中调用的says方法在Dog类及其父类中没有实现)runtime会调用: (实例方法)+ (BOOL)resolveInstanceMethod:(SEL)sel或者 (类方法)+ (BOOL)resolveClassMethod:(SEL)sel;
  • 2、(备用接收者-->消息分发)如果1没有做任何处理,那么runtime就会调用- (id)forwardingTargetForSelector:(SEL)aSelector;
    返回值满足的条件 : 1.非nil 2.非self 3.必须返回一个实现了该方法的对象(例如返回对象dogOther,让dogOther实现says方法,这样消息就能被成功的转发出去,被dogOther对象接受,并输出结果)
  • 3、(完整的消息转发)- (void)forwardInvocation:(NSInvocation *)anInvocation 中选择转发消息的对象,Invocation封装了未知消息的所有细节;
注意:必须重写methodSignatureForSelector才能走到forwardInvocation方法;
@class DogOther;
@interface Dog : Animal

@property (nonatomic, strong) DogOther *dogOther;

- (void)bark;

@end

#import "Dog.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "DogOther.h"

@implementation Dog

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.dogOther = [DogOther new];
    }
    return self;
}

- (void)bark {
    NSLog(@"Dog bark!");
}

////1、类方法调用最终找不到 会走这个方法
//+ (BOOL)resolveClassMethod:(SEL)sel {
//    
//    return [super resolveClassMethod:sel];
//}
//
////对象调用最终找不到 会走这个方法 我们可以在该方法中实现对应的处理逻辑
//+ (BOOL)resolveInstanceMethod:(SEL)sel {
//    NSString *selStr = NSStringFromSelector(sel);//获取方法名
//    if ([selStr isEqualToString:@"says"]) {
//        //动态添加方法
//        class_addMethod([self class], @selector(says), (IMP)notFoundSaysMethod, "@:");
//    }
//    
//    return [super resolveInstanceMethod:sel];
//}
//
//void notFoundSaysMethod(id self, SEL _cmd) {
//    printf("notFoundSaysMethod\n");
//}

////2、备用的接收者--->消息转发
//- (id)forwardingTargetForSelector:(SEL)aSelector {
//    NSString *selStr = NSStringFromSelector(aSelector);
//    if ([selStr isEqualToString:@"says"]) {
//        return self.dogOther;
//    }
//    
//    return [super forwardingTargetForSelector:aSelector];
//}

//3、完整消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([DogOther instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self.dogOther];
    } else {
        [super forwardInvocation:anInvocation];
    }
        
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    
    if (!signature) {
        if ([DogOther instancesRespondToSelector:aSelector]) {
            signature = [DogOther instanceMethodSignatureForSelector:aSelector];
        }
    }
    
    return signature;
}

DogOther:

@interface DogOther : Animal

- (void)says;

@end

@implementation DogOther

- (void)says {
    NSLog(@"DogOther-->says");
}

@end

main:

准备工作:
#import <objc/runtime.h>
#import <objc/message.h>
在Build Settings 中设置enable strict checking of objc_msgsend calls为 no
理解objc_class的结构体组成:
#struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !OBJC2
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
@brief 消息机制的运行原理 : 用[dog say] 为例来描述方法调用的流程

  • 1、首先编译器会把[dog say]转换为 objc_msgsend(dog, @selector(say)) 两者的效果相同
  • 2、Runtime会在dog对象对应的类Dog中的方法缓存中查找方法的SEL 即查找是否存在say方法(objc_cache
  • 3、如果没有找到就会去Dog类的方法列表中查找say方法的SEL (通过类的isa指针指向的methodlist
  • 4、如果没有找到,就会去Dog的父类Animal的方法列表中查找say方法的SEL (通过Dog类的super_class指向其父类)
  • 5、如果没有找到就继续在Animal的父类(NSObject)中查找,依次类推
  • 6、如果在缓存、本类或者父类中找到,则定位到方法的入口开始执行
  • 7、如果一直找到NSObject还是没有找到,那么就会根据以下两种情况进行处理: 1、如果方法的调用时使用的是[dog say]----->那么编译器就会因为找到不到响应的方法而报错; 2、如果方法的调用时使用的是[dog performSelector:@selector(say)]------>就会进入消息的转发流程;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        Dog *dog = [[Dog alloc] init];
//        [dog say];   //等价于 objc_msgSend(dog, @selector(say));
//        [dog bark];
        
        //这种情况属于7中的第二种,进入消息的转发流程 参考 Dog类中的注释
        [dog performSelector:@selector(says)];//notFoundSaysMethod
    }
    return 0;
}

不懂就药问

相关文章

网友评论

    本文标题:iOS高级开发 RunTime机制讲解五---runtime原理

    本文链接:https://www.haomeiwen.com/subject/nhbdlttx.html