Runtime--常见概念以及消息转发

消息机制

引入#import

objc_msgSend(id self, SEL op, ...),消息发送函数,对象动态调用自身方法的函数。

参数1:调用函数的对象。
参数2:方法选择器。
参数3~:方法参数,多个参数依次为 参数4 参数5… 。

Id, 是一个指向objc_object结构体指针,它包含一个Class isa成员,根据isa指针可以找到对象的类。

Id 与instancetype,都可以用来表示一个未知类型的对象,instancetype可以检测对象类型从而调用对象的方法,id不能检测;instancetype只能用作方法返回参数类型,不能用来定义变量。

Class, 类。一个指向objc_class结构体指针,定义:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#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;

** isa**,Class本身也是一个对象,具有对应的super_class,相对于super_class来说Class就是一个对象,isa表示Class这个对象的Class。每个Class都有isa指针指向唯一元类(Meta class),直到最后指向根类,如NSObject,NSObject没有父类,其父类指向nil。

Method,一个指向objc_method结构体指针,存储了方法名、方法类型、IMP。

Ivar,一个指向objc_ivar结构体指针,类中的实例变量,包含了变量名、变量类型等信息。

SEL,方法选择器,根据方法名获取对应方法,是一个指向objc_selector的结构体指针。

IMP,一个函数指针,指向方法的实现。 SEL与IMP的关系

_cmd,表示当前调用方法。

methodLists,方法列表,可以动态添加方法,category便是据此实现,所以category中不能直接添加属性。

cache,常用方法缓存列表。为了提升方法调用效率,当对象receiver调用方法message时,根据对象isa指针找到对应的类,首先在cache中查找对应方法,如果没找到再去methodLists中查找,如果还是没找到就去父类中查找,依次查找到根类,如果最后没找到Crash,抛出unrecognized selector sent to…类似这样的异常信息,而在Crash前我们还可以做的操作:

1.使用 Method Resolution 动态实现方法。

"对象调用了没有实现的方法时会执行此方法"
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(sendMessage:)) {
        void (^block)(void) = ^ {
            NSLog(@"log");
        };
        "c语言函数默认参数(id self, SEL _cmd)
        参数"v@:": v,表示函数返回值类型void    @,表示OC对象参数   :,表示SEL"
        class_addMethod([self class], sel, imp_implementationWithBlock(block), "v@:");
    }
    return YES;
}

2.如果 Method Resolution 返回NO,运行时执行下一步:消息转发(Message Forwarding)。

- (id)forwardingTargetForSelector:(SEL)aSelector
{
   if (aSelector == @selector(sendMessage:)) {
       return [MessageForwarding new];//MessageForwarding类需要自定义实现,并在其中实现方法sendMessage:
   }
   return nil;
}

3.如果没有实现forwardingTargetForSelector消息转发,可以使用methodSignatureForSelector进行消息转发,如果返回nil则Crash,如果返回一个签名函数会生成一个NSInvocation对象,并调用-forwardInvocation:方法。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
    if (!methodSignature) {
        methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];//获取方法参数和返回值,生成一个方法签名对象
    }
    return methodSignature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    MessageForwarding *messageForwarding = [MessageForwarding new];
    if ([messageForwarding respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:messageForwarding];
    }
}

三种方法的选择:

Runtime提供三种方式来将原来的方法实现代替掉,那该怎样选择它们呢?

Method Resolution,由于Method Resolution不能像消息转发那样可以交给其他对象来处理,所以只适用于在原来的类中代替掉。
Fast Forwarding,它可以将消息处理转发给其他对象,使用范围更广,不只是限于原来的对象。
Normal Forwarding,它跟Fast Forwarding一样可以消息转发,但它能通过NSInvocation对象获取更多消息发送的信息,例如:target、selector、arguments和返回值等信息。

Associated Objects
在Category中动态合成属性可以使用以下函数:

//利用消息发送机制生成属性setter方法
void objc_setAssociatedObject (id object, const void *key, id value, objc_AssociationPolicy policy )
参数1:添加属性的对象.
参数2:成员变量地址。
参数3:成员变量。
参数4:修饰属性关键字。
//利用消息发送机制生成属性getter方法
id objc_getAssociatedObject (id object, const void *key )
//利用消息发送机制生删除关联值
void objc_removeAssociatedObjects (id object )

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

Method Swizzling
用一个方法的实现替换另一个方法,一般用在+(void)load中。load方法解析

    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    // 替换方法前先检测方法是否存在,或者是否仅在父类实现了方法
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

    //如果存在直接替换方法
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

你可能感兴趣的:(Runtime--常见概念以及消息转发)