美文网首页
iOS - Runtime之消息处理

iOS - Runtime之消息处理

作者: AKyS佐毅 | 来源:发表于2019-02-15 12:37 被阅读14次

    年后归来,温故知新。

    1、类方法和实例方法之间的区别

    /*
     *1.实例方法和类方法的调用
     */
    CFPerson *person = [[CFPerson alloc]init];
    [person eating];        //实例方法调用
    [CFPerson sleeping];    //类方法调用
    
    ((void (*)(id,SEL))(void *)objc_msgSend)((id)person,sel_registerName("eating"));
    ((void (*)(id,SEL))(void *)objc_msgSend)(objc_getClass("CFPerson"),sel_registerName("sleeping"));
    
    //        [person performSelector:@selector(sleeping)];
    //        [CFPerson performSelector:@selector(eating)];
    
    /*
     *实例->类->元类
     获取isa,在isa对象的方法列表中查找指定方法
     传入实例对象,在类的方法列表中查找
     传入类对象,在元类的方法列表中查找
     */
    
    /*
     *2.验证
     class_getMethodImplementation
     从指定的类方法列表中查找指定的方法,返回此方法的地址
     */
    IMP eatIMP = class_getMethodImplementation([CFPerson class], @selector(eating));
    eatIMP();
    Class metaCls = objc_getMetaClass(class_getName([CFPerson class]));
    IMP sleepIMP = class_getMethodImplementation(metaCls, @selector(sleeping));
    sleepIMP();
    

    2、objc_msgSend介绍

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            /*
             *1.正常调用方法
             */
            CFPerson *person  =  [CFPerson new];
            CFStudent *student  = [CFStudent new];
            [person eatingWithFood:@"apple" inPlace:@"kitchen"];
            [person playing];
            [CFPerson sleeping];
            
            /*
             *2.使用objc_msgSend
             */
            SEL eatSel = @selector(eatingWithFood:inPlace:);
            SEL playSel = @selector(playing);
            SEL sleepSel = NSSelectorFromString(@"sleeping");
            objc_msgSend(person,playSel);
            objc_msgSend(person,eatSel,@"apple",@"kitchen");
            objc_msgSend([CFPerson class],sleepSel);
            
            /*
             objc_msgSend_stret:消息返回的是结构体
             objc_msgSend_fpret:消息返回的是浮点数
             objc_msgSendSuper:给父类发消息
             */
            struct objc_super objSuper;
            objSuper.receiver = student;
            objSuper.super_class = [CFPerson class];
            objc_msgSendSuper(&objSuper,playSel);
        }
        return 0;
    }
    

    3、消息发送流程

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            CFPerson *person = [[CFPerson alloc]init];
            SEL eatSel = @selector(eatingWithFood:inPlace:);
            IMP eatIMP = [person methodForSelector:eatSel];
            
            
            /*
             消息发送流程部分是通过汇编实现的,所以实际上差距也不大
             */
            NSDate *begin = [NSDate date];
            for (int i =0; i<100000000; i++) {
                eatIMP(person,eatSel,@"apple",@"kitchen");
            }
            NSDate *end = [NSDate date];
            NSLog(@"跳过消息发送流程,时间花费为:%.5f",[end timeIntervalSinceDate:begin]);
            
            begin = [NSDate date];
            for (int i =0; i<100000000; i++) {
                [person eatingWithFood:@"apple" inPlace:@"kitchen"];
            }
            end = [NSDate date];
            NSLog(@"正常消息发送流程,时间花费为:%.5f",[end timeIntervalSinceDate:begin]);
        }
        return 0;
    }
    

    执行结果:

    直接跳过消息发送流程[1658:85755] 跳过消息发送流程,时间花费为:2.82438
    直接跳过消息发送流程[1658:85755] 正常消息发送流程,时间花费为:3.01274
    
    image image

    4、直接跳过消息发送流程

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            CFPerson *person = [[CFPerson alloc]init];
            SEL eatSel = @selector(eatingWithFood:inPlace:);
            IMP eatIMP = [person methodForSelector:eatSel];
            
            /*
             消息发送流程部分是通过汇编实现的,所以实际上差距也不大
             */
            NSDate *begin = [NSDate date];
            for (int i =0; i<100000000; i++) {
                eatIMP(person,eatSel,@"apple",@"kitchen");
            }
            NSDate *end = [NSDate date];
            NSLog(@"跳过消息发送流程,时间花费为:%.5f",[end timeIntervalSinceDate:begin]);
            
            begin = [NSDate date];
            for (int i =0; i<100000000; i++) {
                [person eatingWithFood:@"apple" inPlace:@"kitchen"];
            }
            end = [NSDate date];
            NSLog(@"正常消息发送流程,时间花费为:%.5f",[end timeIntervalSinceDate:begin]);
        }
        return 0;
    }
    
    image

    5、消息转发流程

    image
    @implementation RuntimeObject
    
    void testIMP (void){
        NSLog(@"testIMP invoke");
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(test)) {
            
            NSLog(@"resolveInstanceMethod:");
            return NO;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    - (id)forwardingTargetForSelector:(SEL)aSelector{
        if (aSelector == @selector(test)) {
            NSLog(@"forwardingTargetForSelector:");
            return nil;
        }
        
        return [super forwardingTargetForSelector:aSelector];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        
        if (aSelector == @selector(test)) {
            NSLog(@"methodSignatureForSelector:");
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        NSLog(@"forwardInvocation:");
    }
    
    @end
    
    @interface CFPerson : NSObject
    - (void)eating;
    @end
    
    @implementation CFPerson
    - (void)eating
    {
        NSLog(@"Person eating!");
    }
    @end
    -------------------------------------------------------------------
    @interface CFStudent : NSObject
    
    @end
    
    @implementation CFStudent
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == @selector(eating)) {
            return [CFPerson new];
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    @end
    
    -------------------------------------------------------------------
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            /*
             打印runtime信息
             开启:call (void)instrumentObjcMessageSends(YES)
             关闭:call (void)instrumentObjcMessageSends(NO)
             /private/tmp/ 文件夹,找到最新的 msgSends-xxxx文件
             */
            CFStudent *student  = [CFStudent new];
            [student performSelector:@selector(eating)];
            [student performSelector:@selector(eating)];
        }
        return 0;
    }
    

    6、类方法和实例方法的动态消息解析

    @interface CFPerson : NSObject
    //姓名
    @property(nonatomic,copy)NSString *name;
    //年龄
    @property(nonatomic,copy)NSString *age;
    @end
      
    -------------------------------------------------------------------
    
    NSString *_name;
    @implementation CFPerson
    @dynamic name;
    @synthesize age;
    
    void playIMP(id cls,SEL cmd){
        NSLog(@"Person playing!");
    }
    
    void eatIMP(id cls,SEL cmd){
        NSLog(@"Person eating!");
    }
    
    - (void)sleeping
    {
        NSLog(@"Person sleeping!");
    }
    
     + (BOOL)resolveClassMethod:(SEL)sel
    {
        //动态添加类方法
        /*
         *v:返回void @:表示对象 :表示_cmd
         */
        if (sel == NSSelectorFromString(@"playing")) {
    //        class_addMethod(object_getClass([CFPerson class]), sel, (IMP)playIMP, "v@:");
            class_addMethod(objc_getMetaClass("CFPerson"), sel, (IMP)playIMP, "v@:");
            return YES;
        }
        //methodForSelector
        if(sel == @selector(sleeping)){
            class_addMethod(objc_getMetaClass("CFPerson"), sel, [[CFPerson new] methodForSelector:@selector(sleeping)], "v@:");
            return YES;
        }
        return  [super resolveClassMethod:sel];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == NSSelectorFromString(@"eating")) {
            class_addMethod([CFPerson class], sel, (IMP)eatIMP, "v@:");
            return YES;
        }
        if (sel == @selector(setName:)) {
            class_addMethod(self, sel, (IMP)setName, "v@:@");
            return YES;
        }
        if (sel == @selector(name)) {
            class_addMethod(self, sel, (IMP)getName, "@@:");
            return YES;
        }
        return  [super resolveInstanceMethod:sel];
    }
    
    void setName(id cls,SEL cmd, NSString *name)
    {
        _name = name;
    }
    
    id getName(id cls,SEL cmd)
    {
        return _name;
    }
    @end
    
    -------------------------------------------------------------------
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            //1.类方法
            [CFPerson performSelector:NSSelectorFromString(@"playing")];
            [CFPerson performSelector:NSSelectorFromString(@"sleeping")];
            //2.实例方法
            CFPerson *person = [[CFPerson alloc]init];
            [person performSelector:NSSelectorFromString(@"eating")];
            [person performSelector:NSSelectorFromString(@"sleeping")];     //可以执行成功
            
            /*
             *3.@dynamic
             */
            person.name = @"lilei";
            NSLog(@"person.name:%@",person.name);
            
            person.age = @"20";
            NSLog(@"person.age:%@",person.age);
        }
        return 0;
    }
    

    7、重定向

    @interface CFPerson : NSObject
    
    @end
    
    -------------------------------------------------------------------
      
    @implementation CFPerson
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        if (aSelector == NSSelectorFromString(@"studying")) {
            return [CFStudent new] ;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    @end
    
    -------------------------------------------------------------------
    
    @interface CFStudent : NSObject
    //学习
    - (void)studying;
    @end
      
    -------------------------------------------------------------------
      
    @implementation CFStudent
    //学习
    - (void)studying
    {
        NSLog(@"Student studying!");
    }
    @end
    -------------------------------------------------------------------
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            CFPerson *person = [[CFPerson alloc]init];
            [person performSelector:NSSelectorFromString(@"studying")];
        }
        return 0;
    }
    
    

    8、转发

    image
    @interface CFPerson : NSObject
    //吃东西
    - (void)eating;
    @end
    
    -------------------------------------------------------------------
    
    @implementation CFPerson
    //吃东西
    - (void)eating;
    {
        NSLog(@"Person eating!");
    }
    
    //生成对应的方法签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        if (aSelector == NSSelectorFromString(@"playing")) {
    //        return [self methodSignatureForSelector:@selector(eating)];
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        if (aSelector == NSSelectorFromString(@"studying")) {
            return [[CFStudent new] methodSignatureForSelector:@selector(studying)];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        if (anInvocation.selector == NSSelectorFromString(@"playing")) {
            //1.改变执行的函数
            anInvocation.selector = @selector(eating);
            [anInvocation invokeWithTarget:self];
        }
        if (anInvocation.selector == NSSelectorFromString(@"studying")) {
            //2.改变执行的目标
            [anInvocation invokeWithTarget:[CFStudent new]];
        }
        //3.同时改变执行函数和执行目标....
    }
    
    @end
    
    -------------------------------------------------------------------
    
    @interface CFStudent : NSObject
    //学习
    - (void)studying;
    @end
    
    -------------------------------------------------------------------
    
    @implementation CFStudent
    //学习
    - (void)studying
    {
        NSLog(@"Student studying!");
    }
    @end
      
    -------------------------------------------------------------------
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            CFPerson *person = [[CFPerson alloc]init];
            //把执行playing方法,变成执行person里的eating
            [person performSelector:NSSelectorFromString(@"playing")];
            //把执行studying方法,交给student类
            [person performSelector:NSSelectorFromString(@"studying")];
        }
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:iOS - Runtime之消息处理

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