美文网首页M_iOSiOS小筑
iOS开发基础之消息传递

iOS开发基础之消息传递

作者: 华子小筑 | 来源:发表于2015-11-19 12:01 被阅读121次

    在项目编码中最为常见的就是[object message];这种形式的消息发送,对于其他面向对象语言来说就是实例对象调用类中实现的实例方法;[object message]这种形式到底做了什么呢?

    objc_object, objc_class 以及 objc_method

    在Objective-C中类、对象、方法都是C语言中结构体类型;具体数据类型可以参照objc/objc.h文件

      // 类
      struct objc_class {
                             Class isa;//指针,顾名思义,表示是一个什么,
                             //实例的isa指向类对象,类对象的isa指向元类
                             #if !__OBJC2__
                             Class super_class;  //指向父类
                             const char *name;  //类名
                             long version;
                             long info;
                             long instance_size
                             struct objc_ivar_list *ivars //成员变量列表
                             struct objc_method_list **methodLists; //方法列表
                             struct objc_cache *cache;//缓存一种优化,调用过的方法存入缓存列表,下次调用先找缓存
                             struct objc_protocol_list *protocols //协议列表
                             #endif
                 } OBJC2_UNAVAILABLE;
                 
      //对象 
                 struct objc_object {
                         Class isa  OBJC_ISA_AVAILABILITY;
                 };
     // 方法
                 struct objc_method {
                         SEL method_name                 OBJC2_UNAVAILABLE;  // 方法名
                         char *method_types                  OBJC2_UNAVAILABLE;
                         IMP method_imp                      OBJC2_UNAVAILABLE;  // 方法实现
                 }
    

    消息的传递

    People类中声明并实现walk方法则消息会正常被传递
        #import <Foundation/Foundation.h>
        @interface HZPeople : NSObject
        -(void)walk;
        @end
        
        #import "HZPeople.h"
        #import <objc/objc-runtime.h>
        @implementation HZPeople
        -(void)walk{
            NSLog(@"people walk!!");
        }
    

    控制台能正常打印 people walk!!

    如果只声明并不是实现walk方法则会调用+(BOOL)resolveInstanceMethod:(SEL)sel;允许在此进行对类增加方法
        #import <Foundation/Foundation.h>
        @interface HZPeople : NSObject
        -(void)walk;
        @end
        
        #import "HZPeople.h"
        #import <objc/objc-runtime.h>
        @implementation HZPeople
        
        void anotherPeopleWalk(id obj ,SEL _cmd){
    NSLog(@"anotherPeopleWalk!");
    }
        +(BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"resolveInstanceMethod!");
    if (sel == @selector(walk)) {
      // 通过imp_implementationWithBlock执行新增方法事项
        IMP fooIMP = imp_implementationWithBlock(^(id _self) {
            NSLog(@"Doing foo");
        });
        // 给类动态添加执行方法
        class_addMethod([self class], sel, fooIMP, "v@:");
      //  class_addMethod([self class], sel, (IMP)anotherPeopleWalk, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
    }
    

    控制台会先打印resolveInstanceMethod然后打印Doing foo

    +(BOOL)resolveInstanceMethod:(SEL)sel中没有实现方法的新增则会调用-(id)forwardingTargetForSelector:(SEL)aSelector允许对消息转发给其他对象
       #import <Foundation/Foundation.h>
        #import "HZMan.h"
        @interface HZPeople : NSObject
        @property(nonatomic,strong)HZMan* man;
        -(void)walk;
        @end
        
        #import "HZPeople.h"
        #import <objc/objc-runtime.h>
        @implementation HZPeople
        
        -(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(walk)) {
        // 将消息转发给HZMan中的walk方法
        return self.man;
    }
    return [super forwardingTargetForSelector:aSelector];
    }
    
    -(id)forwardingTargetForSelector:(SEL)aSelector如果没有实现还有最后的机会进行一次消息转发;这个需要重写- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector为执行方法进行签名,最后在实现-(void)forwardInvocation:(NSInvocation *)anInvocation完成一次消息的完整转发
     #import <Foundation/Foundation.h>
        #import "HZMan.h"
        @interface HZPeople : NSObject
        @property(nonatomic,strong)HZMan* man;
        -(void)walk;
        @end
        
        #import "HZPeople.h"
        #import <objc/objc-runtime.h>
        @implementation HZPeople
        
        - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
    if(!signature){
        signature = [_man methodSignatureForSelector:aSelector];
    }
    return signature;
    }
    -(void)forwardInvocation:(NSInvocation *)anInvocation{
        SEL sel = anInvocation.selector;
        if ([self .man respondsToSelector:sel]) {
            [anInvocation invokeWithTarget:self.man];
        }else{
            [self doesNotRecognizeSelector:sel];
        }
    }
    
    如果最终也没有对消息进行处理就只能执行doesNotRecognizeSelector:sel抛出异常了

    几个概念

    SEL
    • SEL:选择器,是表示一个方法的selector的指针
    • typedef struct objc_selector *SEL;
    • Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL
    • 本质上,SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法),它的存在只是为了加快方法的查询速度。
    IMP
    • IMP:实际上是一个函数指针,指向方法实现的首地址
    • id (*IMP)(id, SEL, ...)
    • 参数1:实例方法或者是类方法 分别代表类实例的内存地址或是指向原类的指针
    Method

    Method :表示类定义的方法

    相关文章

      网友评论

        本文标题:iOS开发基础之消息传递

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