首页 > 代码库 > runtime的一些应用

runtime的一些应用

一、便利类的成员变量、属性、方法

/**遍历类所以成员变量

     * @param <#__unsafe_unretained Class cls#> 要遍历的类

     * @param  <#unsigned int *outCount#> 成员变量数量

     */

    //    class_copyIvarList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)

    

    unsigned int count = 0;

 

    Ivar * ivars = class_copyIvarList([UIView class], &count);

    

    for (int i = 0; i < count; i++) {

        Ivar ivar = ivars[i];

        

        const char * name = ivar_getName(ivar);

        

        NSString * str = [NSString stringWithUTF8String:name];

        

        NSLog(@"%d:%@",i,str);

    }

    free(ivars);

    /**遍历类所以属性

     * @param <#__unsafe_unretained Class cls#> 要遍历的类

     * @param  <#unsigned int *outCount#> 属性数量

     */

    //class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)

    unsigned int propertyCount = 0;

    objc_property_t * propertys = class_copyPropertyList([UIView class], &propertyCount);

    for (int i = 0; i < propertyCount; i++) {

        objc_property_t property = propertys[i];

        const char * name = property_getName(property);

        NSString * str = [NSString stringWithUTF8String:name];

        NSLog(@"%d:%@",i,str);

    }

    free(propertys);

    

    /**遍历类所有方法

     * @param <#__unsafe_unretained Class cls#> 要遍历的类

     * @param  <#unsigned int *outCount#> 属性数量

     */

    //class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)

    unsigned int methodCount = 0;

    

    Method * methods = class_copyMethodList([UIView class], &methodCount);

    for (int i = 0; i < count; i++) {

        Method method = methods[i];

        SEL sel = method_getName(method);

        NSLog(@"%d:%@",i,NSStringFromSelector(sel));

    }

    free(methods);

 二、消息转发

1、重定向

消息转发机制执行前,Runtime 系统允许我们替换消息的接收者为其他对象。通过 - (id)forwardingTargetForSelector:(SEL)aSelector 方法。

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if(aSelector == @selector(mysteriousMethod:)){
        return alternateObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

如果此方法返回 nil 或者 self,则会计入消息转发机制(forwardInvocation:),否则将向返回的对象重新发送消息。

2、转发

当动态方法解析不做处理返回 NO 时,则会触发消息转发机制。这时 forwardInvocation: 方法会被执行,我们可以重写这个方法来自定义我们的转发逻辑:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

唯一参数是个 NSInvocation 类型的对象,该对象封装了原始的消息和消息的参数。我们可以实现 forwardInvocation: 方法来对不能处理的消息做一些处理。也可以将消息转发给其他对象处理,而不抛出错误。

注意:参数 anInvocation 是从哪来的?
在 forwardInvocation: 消息发送前,Runtime 系统会向对象发送methodSignatureForSelector: 消息,并取到返回的方法签名用于生成 NSInvocation 对象。所以重写 forwardInvocation: 的同时也要重写 methodSignatureForSelector: 方法,否则会抛异常。

当一个对象由于没有相应的方法实现而无法相应某消息时,运行时系统将通过 forwardInvocation: 消息通知该对象。每个对象都继承了 forwardInvocation: 方法。但是, NSObject 中的方法实现只是简单的调用了 doesNotRecognizeSelector:。通过实现自己的 forwardInvocation: 方法,我们可以将消息转发给其他对象。

forwardInvocation: 方法就是一个不能识别消息的分发中心,将这些不能识别的消息转发给不同的接收对象,或者转发给同一个对象,再或者将消息翻译成另外的消息,亦或者简单的“吃掉”某些消息,因此没有响应也不会报错。这一切都取决于方法的具体实现。

注意:
forwardInvocation:方法只有在消息接收对象中无法正常响应消息时才会被调用。所以,如果我们向往一个对象将一个消息转发给其他对象时,要确保这个对象不能有该消息的所对应的方法。否则,forwardInvocation:将不可能被调用。

3、转发和多继承

转发和继承相似,可用于为 Objc 编程添加一些多继承的效果。就像下图那样,一个对象把消息转发出去,就好像它把另一个对象中的方法接过来或者“继承”过来一样。

技术分享
 

这使得在不同继承体系分支下的两个类可以实现“继承”对方的方法,在上图中 Warrior 和 Diplomat 没有继承关系,但是 Warrior 将 negotiate 消息转发给了 Diplomat 后,就好似 Diplomat 是 Warrior 的超类一样。

消息转发弥补了 Objc 不支持多继承的性质,也避免了因为多继承导致单个类变得臃肿复杂。

 

4、转发与继承

虽然转发可以实现继承的功能,但是 NSObject 还是必须表面上很严谨,像 respondsToSelector: 和 isKindOfClass: 这类方法只会考虑继承体系,不会考虑转发链。

如果上图中的 Warrior 对象被问到是否能响应 negotiate消息:

if ( [aWarrior respondsToSelector:@selector(negotiate)] )
    ...

回答当然是 NO, 尽管它能接受 negotiate 消息而不报错,因为它靠转发消息给 Diplomat 类响应消息。

如果你就是想要让别人以为 Warrior 继承到了 Diplomat 的 negotiate 方法,你得重新实现 respondsToSelector: 和 isKindOfClass: 来加入你的转发算法:

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
        /* Here, test whether the aSelector message can     *
         * be forwarded to another object and whether that  *
         * object can respond to it. Return YES if it can.  */
    }
    return NO;
}

除了 respondsToSelector: 和 isKindOfClass: 之外,instancesRespondToSelector: 中也应该写一份转发算法。如果使用了协议,conformsToProtocol: 同样也要加入到这一行列中。

如果一个对象想要转发它接受的任何远程消息,它得给出一个方法标签来返回准确的方法描述 methodSignatureForSelector:,这个方法会最终响应被转发的消息。从而生成一个确定的 NSInvocation 对象描述消息和消息参数。这个方法最终响应被转发的消息。它需要像下面这样实现:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    }
    return signature;
}

 

runtime的一些应用