在部分模拟定义类方法的对象时,使用OCMock 3时遇到了一个有趣的问题。我不确定这是否是动态子类的问题,它是部分模拟或我对objc运行时的误解。任何帮助将不胜感激。
作为运行测试和其他调试版本的一部分,我们使用OmniFoundations'进行方法声明的运行时验证。 OBRuntimeCheck。简而言之,这些检查之一尝试使用运行时来验证类型签名是否与继承和协议一致性中的类方法匹配。这通过列出在运行时中注册的类以及为每个类复制metaClass
的实例方法来实现。对于metaClass中的每个Method
,如果它存在于metaClass的超类中,则会比较类型签名。
在metaClass的超类中为其中一个ocmock替换选择器ocmock_replaced_*
调用class_getInstanceMethod时出现问题。控制台中记录了测试崩溃EXC_BAD_INSTRUCTION code=EXC_i386_INVOP subcode=0x0
和no class for metaclass
。给出的例子:
class_getInstanceMethod(metaSuperClass, NSSelectorFromString(@"ocmock_replaced_classMessage"))
当部分模拟定义类方法的对象时,看起来OCMock 3框架生成了一个动态子类,有些是模拟对象的混合,还有一些是动态生成的类的混合。的metaClass。
这种行为和崩溃在OCMock 3中是新的,我真的不知道接下来要去哪里看。任何运行时专家都知道这里会发生什么?在查看代码时,我确实感到惊讶的是,用于模拟的动态生成的类正在让它的元类调高,但我不一定认为这是错误的。为了便于调试,我在OCMock的新分支中创建了一个简化的测试用例。可以找到崩溃测试here。非常感谢任何指导帮助。
答案 0 :(得分:0)
我可能会离开这里,但我认为metaClass的超类是NSObject(这就是为什么你通常可以在类对象上调用NSObject实例方法)。我不确定你应该做什么,通常是使用metaClass的超类。
通常,metaClass存储有关类方法的所有信息。因此,在metaClass上获取“instance”方法与在关联的常规Class上获取类方法相同。运行时可以简单地取消引用实例的“isa”指针,以找到方法列表来查找实例方法;在Class对象上执行相同操作会获得元类(具有相同结构),因此相同的过程会导致查找类方法。
OCMock将为任何部分模拟创建一个神奇的子类,并将该实例上的类更改为新的子类,因此所有实例方法调配都将特定于该实例。但是对于类方法,我认为它必须修改原始类本身 - 否则,在常规代码中调用常规类方法不会被截获。它保留了原始实现的副本,以便当您在模拟上调用-stopMocking时,它可以恢复原始实现(添加的ocmock_replaced * impl仍将存在但不应再被调用)。
您可以简单地忽略任何以“ocmock_replaced”开头的选择器,因为这实际上与您可能检查的实际代码无关。您可能还有更好的运气将“class_getInstanceMethod(metaSuperClass,...”更改为“class_getClassMethod(regularSuperClass,...”)。我不知道为什么会遇到崩溃 - 我希望class_getInstanceMethod(metaSuperClass, ...)在大多数情况下只返回NULL。