【问题标题】:Obtaining class of returned object in Objective-C在 Objective-C 中获取返回对象的类
【发布时间】:2012-02-10 18:32:25
【问题描述】:

我正在尝试通过以下方式在 Objective-C 中实现对象的代理:

- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel
{
    return [_proxifiedObject methodSignatureForSelector:sel];
}

- (void) forwardInvocation:(NSInvocation *)invocation
{
    char returnType = invocation.methodSignature.methodReturnType[0];
    if(returnType == '@')
    {
        id anotherProxy = [[[MyProxy alloc] initWithInvocation:invocation] autorelease];
        [invocation setReturnValue:&anotherProxy];
    } else {
        invocation.target = _proxifiedObject;
        [invocation invoke];
    }
}

另一个代理应该从这个调用构造对象。但是,当对象正在构建中时,我​​们应该能够收集和存储对它的调用,以便在构建对象后有机会处理它们。为此,我应该实现 2 个方法:methodSignatureForSelector: 和 forwardInvocation:。第二个很容易,但第一个是问题的全部内容。

我们应该知道代理对象的类以便能够提供签名,但是另一个代理初始化的调用不提供有关返回对象类的信息。有没有办法使用 Apple 的 Objective-C 运行时获取方法返回的对象的类?

更新

我了解返回对象的实际类可能不同,但可能是声明该方法的唯一类。如果方法声明为

 - (SomeClass*) someMethod;

在界面中,我希望获取 SomeClass。

更新

终于解决了我的问题。该解决方案并不优雅,但它“有效”。 我写了这个方法:

static NSMutableDictionary* dictionarySelectorsToSignatures;

+ (NSMethodSignature*) methodSignatureForUnknownClassForSelector: (SEL) sel
{
    NSMethodSignature* candidate = [dictionarySelectorsToSignatures objectForKey:[NSNumber numberWithLongLong:(long long) sel]];
    if(candidate == nil)
    {
        int classesCount = objc_getClassList(NULL, 0);
        Class* classes = malloc(sizeof(Class) * classesCount);
        objc_getClassList(classes, classesCount);
        for(int i = 0; i < classesCount; i++)
        {
            if(class_getClassMethod(classes[i], @selector(instanceMethodSignatureForSelector:)) != NULL)
            {
                NSMethodSignature* signature = [classes[i] instanceMethodSignatureForSelector:sel];
                if(signature != nil)
                {
                    if(candidate != nil)
                    {
                        if(![candidate isEqual:signature])
                        {
                            return nil;
                        }
                    } else {
                        candidate = signature;
                    }
                }
            }
        }
        if(candidate != nil)
        {
            [dictionarySelectorsToSignatures setObject:candidate 
                                                forKey:[NSNumber numberWithLongLong:(long long)  sel]];
        }
    }
    return candidate;
}

如果选择器没有歧义,则此方法返回签名,其他情况下返回 nil。 它的主要缺点是它不支持在运行时修改类/类列表,因此只有在所有类都已注册并且代码不修改类时才能使用它(与 KVO 一起使用可能有风险!)。

【问题讨论】:

    标签: objective-c runtime objective-c-runtime


    【解决方案1】:

    有没有办法使用 Apple 的 Objective-C 运行时获取方法返回的对象的类?

    否 - 如果一个方法返回一个类类型,那么在它返回之前您无法知道实际的具体类型。由于具体的类类型未知,您无法找出它的任何方法可能具有的返回类型。

    【讨论】:

    • 但是是否有一个函数(或其他)可以检索方法的声明类型?
    • @Vsevolod:在像[[a f] g] 这样的情况下,您可以在不调用f 的情况下获得f 的签名,但不能获得g 的签名。这是因为fg 只是消息名称,没有特定类型。您只能从组合类和选择器、协议和选择器或实例和选择器中获取方法签名。
    • 这应该是完全通用的还是您可以假设特定实例,在调用方使用协议,...?
    • 这应该是完全通用的。
    • 好的。放下问题。我试图解决的问题是将代理收到的消息重新发送到代理对象。我不知道代理对象的类,因为它还不存在。在创建它之前我无法获得它的类。也许有任何其他方式来归档我的目标(重新发送消息)而不需要获取方法签名?
    【解决方案2】:

    这里是检查“instance”是否为 NSString 的方法

    if ([instance isKindOfClass:[NSString class]]( {
      //do something
    }
    

    那么好?

    【讨论】:

    • 没有。我需要获取一个类而不是检查对象是否属于特定类。
    • 好吧,如果你有几个可能的类,你可以检查哪个是你的变量的实例,你也会知道这个类
    【解决方案3】:

    你可以得到对象的类的实际名称:

    例如对于一个对象foo

    NSLog(@"foo is an object of class %@", NSStringFromClass([foo class]));
    

    【讨论】:

    • 这不是我的情况,因为我本身没有对象。我有一个返回对象的方法(实际上不是方法,而是 NSInvocation 的实例)。
    • 不管怎样,你都有一个对象——你可以用它来知道它是什么类。您不必使用 NSLog 语句,而是使用 NSStringFromClass() 函数。
    猜你喜欢
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    相关资源
    最近更新 更多