iOS开发基础之消息传递

在项目编码中最为常见的就是[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 
    @interface HZPeople : NSObject
    -(void)walk;
    @end
    
    #import "HZPeople.h"
    #import 
    @implementation HZPeople
    -(void)walk{
        NSLog(@"people walk!!");
    }

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

如果只声明并不是实现walk方法则会调用+(BOOL)resolveInstanceMethod:(SEL)sel;允许在此进行对类增加方法
    #import 
    @interface HZPeople : NSObject
    -(void)walk;
    @end
    
    #import "HZPeople.h"
    #import 
    @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 
    #import "HZMan.h"
    @interface HZPeople : NSObject
    @property(nonatomic,strong)HZMan* man;
    -(void)walk;
    @end
    
    #import "HZPeople.h"
    #import 
    @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 
    #import "HZMan.h"
    @interface HZPeople : NSObject
    @property(nonatomic,strong)HZMan* man;
    -(void)walk;
    @end
    
    #import "HZPeople.h"
    #import 
    @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开发基础之消息传递)