ObjC:+ 给出错误的失败
-
我有一个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
。
如何在class级等级上游? 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
参数必须是我的BaseClass
的子类?
编辑:
我意识到我发布的示例在将各个部分拉入单独的Xcode工作区后工作正常。 我已经从上面发布了玩具源代码 ,但是我原来的描述中遗漏了这些代码 。
我实际上有两个静态库( libSharedStuff.a
和libHelperSharedStuff.a
)。 应用程序目标与libSharedStuff.a
链接,unit testing目标依赖于应用程序目标以及链接libHelperSharedStuff.a
。 当BaseClass.m
是两个静态库目标的成员时, +isSubclassOfClass:
将在unit testing断言中失败。 具体来说,当我传递MockBaseClass
时它会失败, MockBaseClass
是BaseClass
的子类,它是unit testing目标的一部分。
所有这些都在上面链接的项目中说明。
正如我在编辑我的问题时解释的那样,我在包含静态库libSharedStuff.a
和libHelperSharedStuff.a
两个包含BaseClass.m
。 主应用程序目标链接libSharedStuff.a
,unit testing目标链接libHelperSharedStuff.a
。 这意味着当.xctest
包在运行时注入我的应用程序时, BaseClass
符号被定义两次
( 也许?这可能吗?运行时实际发生了什么? )。
我似乎在我的unit testing目标中, MockSubclassOfBaseClass
inheritance自MockSubclassOfBaseClass
的BaseClass
符号,而在我的应用程序目标中, SubclassOfBaseClass
inheritance自libSharedStuff.a
的BaseClass
符号。 如果是这种情况,那么为什么+isSublcassOfClass:
在MockSubclassOfBaseClass
时响应NO
是MockSubclassOfBaseClass
的!
任何澄清,确认或提供更好的见解的答案都非常感谢我在这里得到了正确的想法。
确保静态库实际上导致符号正确链接。 我的直接猜测是,作为aClass传递的静态lib提供的类在运行时实际上是Nil,因此向发送它的任何消息返回0。
确保这一点的一种方法是创建一个使用它们的构造函数:
__attribute__((constructor)) static void EnsureClassesAreLoaded(void) { [BaseClass self]; }
弱链接类可能有点棘手。
如果您使用的是cocoa豆荚,请确保没有将所有豆荚链接到测试目标。
这解决了我的问题。