ObjC:+ [NSObject isSubclassOfClass:]给出错误的失败

时间:2013-11-09 21:44:16

标签: ios objective-c

  • 我有一个iOS静态库,它定义了一个NSOperation基类,客户端应该将其子类添加到自己的逻辑中:@interface BaseClass : NSOperation

  • 客户使用经理注册其子类:-[OperationManagerClass registerClass:forType:]

  • 在经理中,我想强制您必须注册BaseClass的子类,而不仅仅是NSOperation

好吧,似乎断言+isSubclassOfClass:应该完成工作。但是......事实并非如此。

@implementation OperationManagerClass

- (void)registerClass:(Class)aClass forType:(NSString *)type
{
    NSAssert([aClass isSubclassOfClass:[BaseClass class]);

    self.registeredClasses[type] = aClass;
}

@end

断言总是NO,即使传递BaseClass

如何在班级层级中走得更远? NSOperationNSObject都回复YES

(lldb) p (BOOL)[aClass isSubclassOfClass:[BaseClass class]]
(BOOL) $0 = NO
(lldb) po aClass
BaseClass
(lldb) p (BOOL)[aClass isSubclassOfClass:[NSOperation class]]
(BOOL) $2 = YES
(lldb) p (BOOL)[aClass isSubclassOfClass:[NSObject class]]
(BOOL) $3 = YES

请注意,基本操作类的使用者是iOS应用程序项目中的子类,OperationManagerClassBaseClass位于包含的静态库中。为什么我认为这可能与isSubclassOfClass:错误有关?由于以下原因......

仍然在libSharedStuff.a

@implementation OperationManagerClass

- (void)registerClass:(Class)aClass forType:(NSString *)type
{
    // Obviously OperationManagerClass.m cannot #import "OutsideClass.h"
    NSAssert([aClass isSubclassOfClass:NSClassFromString(@"OutsideClass");

    self.registeredClasses[type] = aClass;
}

@end

在应用程序目标

@interface OutsideClass : NSOperation
@end

@interface OutsideClassSubA : OutsideClass
@end

...传递OutsideClassSubA时产生以下结果:

(lldb) p (BOOL)[aClass isSubclassOfClass:[OutsideClass class]]
(BOOL) $0 = YES
(lldb) po aClass
OutsideClassSubA
(lldb) p (BOOL)[aClass isSubclassOfClass:[NSOperation class]]
(BOOL) $2 = YES
(lldb) p (BOOL)[aClass isSubclassOfClass:[NSObject class]]
(BOOL) $3 = YES

这里发生了什么?为什么+isSubclassOfClass:会给出错误的答案?如何强制aClass参数必须是我BaseClass的子类?

<小时/> 编辑:
我意识到我发布的示例在将各个部分拉入单独的Xcode工作区后工作正常。我posted the toy source code from above我的原始描述中遗漏了一些。

我实际上有两个静态库(libSharedStuff.alibHelperSharedStuff.a)。应用程序目标与libSharedStuff.a链接,单元测试目标取决于应用程序目标以及链接libHelperSharedStuff.a。当BaseClass.m两个静态库目标的成员时,+isSubclassOfClass:将在单元测试断言中失败。具体来说,当我通过MockBaseClass时,它会失败,BaseClass是{{1}}的子类,它是单元测试目标的一部分。

所有这些都在上面链接的项目中说明。

3 个答案:

答案 0 :(得分:7)

正如我在编辑问题时所解释的那样,我在BaseClass.mlibSharedStuff.a两个静态库中都包含libHelperSharedStuff.a。主要应用程序目标链接libSharedStuff.a,单元测试目标链接libHelperSharedStuff.a。这意味着当.xctest包在运行时注入我的应用程序时,BaseClass符号被定义两次

也许?这可能吗?运行时实际发生了什么?)。

我似乎在我的单元测试目标MockSubclassOfBaseClass中继承了BaseClass中的libHelperSharedStuff.a符号,而在我的应用中,目标SubclassOfBaseClass继承自BaseClass libSharedStuff.a中的符号。如果是这种情况,则+isSublcassOfClass:传递NOMockSubclassOfBaseClass响应{{1}}是有道理的。

任何能够澄清,确认或提供更好洞察力的答案我都非常感激。

答案 1 :(得分:1)

确保静态库实际上导致符号正确链接。我的直接猜测是,作为aClass传递的静态lib提供的类在运行时实际上是Nil,因此向发送它的任何消息返回0。

确保这一点的一种方法是创建一个使用它们的构造函数:

__attribute__((constructor)) static void EnsureClassesAreLoaded(void) {
    [BaseClass self];
}

弱链接类可能有点棘手。

答案 2 :(得分:1)

如果您使用的是可可豆荚,请确保没有将所有豆荚链接到测试目标。

这解决了我的问题。