4
  • 我有一个 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

在阶级层次上走得更高呢?NSOperation并且NSObject都回应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 应用程序项目中的子类,并且OperationManagerClass位于BaseClass包含的静态库中。为什么我认为这可能与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参数必须是 my 的子类BaseClass


编辑:
我意识到我发布的示例在将这些部分拉入单独的 Xcode 工作区后工作正常。我已经从上面发布了玩具源代码,但我在原始描述中没有提及。

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

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

4

3 回答 3

7

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

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

我似乎在我的单元测试目标中MockSubclassOfBaseClass继承自BaseClass符号 in libHelperSharedStuff.a,而在我的应用程序中目标SubclassOfBaseClass继承自BaseClass符号 in libSharedStuff.a。如果是这样,那么为什么在传入时+isSublcassOfClass:响应是有道理的!NOMockSubclassOfBaseClass

任何澄清,确认或提供更好见解的答案,我在这里得到了正确的想法,我们将不胜感激。

于 2013-11-10T16:08:16.540 回答
1

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

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

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

弱链接类可能有点棘手。

于 2013-11-09T22:14:27.213 回答
1

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

这为我解决了这个问题。

于 2015-01-07T10:25:47.987 回答