如何让 ARC 下的 OCMock 停止使用弱属性清空 NSProxy 子类集?
在 ARC
下,我有一个对象 Child
,它有一个 weak
属性 parent
。我正在尝试为 Child
编写一些测试,并使用 OCMock
模拟其 parent
属性。
在 ARC 下,使用合成的弱属性设置器设置 NSProxy
子类不会设置该属性...设置弱属性后的行,检查它发现它已经nil.这是具体的例子:
@interface Child : NSObject
@property (nonatomic, weak) id <ParentInterface>parent;
@end
@implementation Child
@synthesize parent = parent_;
@end
// ... later, inside a test class ...
- (void)testParentExists
{
// `mockForProtocol` returns an `NSProxy` subclass
//
OCMockObject *aParent = [OCMockObject mockForProtocol:@protocol(ParentInterface)];
assertThat(aParent, notNilValue());
// `Child` is the class under test
//
Child *child = [[Child alloc] init];
assertThat(child, notNilValue());
assertThat(child.parent, nilValue());
child.parent = (id<ParentInterface>)aParent;
assertThat([child parent], notNilValue()); // <-- This assertion fails
[aParent self]; // <-- Added this reference just to ensure `aParent` was valid until the end of the test.
}
我知道我可以使用 assign
属性而不是 weak
属性来解决这个问题,让 Child
引用 < code>Parent,但是当我完成它时,我必须nil
出parent
(就像某种穴居人),这正是ARC 应该避免这种情况。
关于如何在不更改我的应用程序代码的情况下通过此测试有什么建议吗?
编辑:如果我使 aParent
成为一个实例,这似乎与 OCMockObject
是一个 NSProxy
有关NSObject
,child.parent
弱引用“保存”一个非零值。仍在寻找一种无需更改应用程序代码即可通过此测试的方法。
编辑2:接受布莱克的回答后,我在我的项目中实现了一个预处理器宏,该宏有条件地将我的属性从weak -> 更改为weak -> 。分配。您的里程可能会有所不同:
#if __has_feature(objc_arc)
#define BBE_WEAK_PROPERTY(type, name) @property (weak, nonatomic) type name
#else
#define BBE_WEAK_PROPERTY(type, name) @property (assign, nonatomic) type name
#endif
Under ARC
, I have an object, Child
that has a weak
property, parent
. I'm trying to write some tests for Child
, and I'm mocking its parent
property using OCMock
.
Under ARC, setting an NSProxy
subclass using a synthesized weak property setter doesn't set the property ... the line after the weak property is set, checking it reveals that it's already nil
. Here's the concrete example:
@interface Child : NSObject
@property (nonatomic, weak) id <ParentInterface>parent;
@end
@implementation Child
@synthesize parent = parent_;
@end
// ... later, inside a test class ...
- (void)testParentExists
{
// `mockForProtocol` returns an `NSProxy` subclass
//
OCMockObject *aParent = [OCMockObject mockForProtocol:@protocol(ParentInterface)];
assertThat(aParent, notNilValue());
// `Child` is the class under test
//
Child *child = [[Child alloc] init];
assertThat(child, notNilValue());
assertThat(child.parent, nilValue());
child.parent = (id<ParentInterface>)aParent;
assertThat([child parent], notNilValue()); // <-- This assertion fails
[aParent self]; // <-- Added this reference just to ensure `aParent` was valid until the end of the test.
}
I know that I can get around this using an assign
property instead of a weak
property for the Child
to reference the Parent
, but then I'd have to nil
out the parent
when I was done with it (like some sort of caveman), which is exactly the sort of thing that ARC was supposed to obviate.
Any suggestions on how to make this test pass without changing my app code?
Edit: It seems to have to do with OCMockObject
being an NSProxy
, if I make aParent
be an instance of NSObject
, the child.parent
weak reference "holds" a non-nil value. Still looking for a way to make this test pass without changing app code.
Edit 2: After accepting Blake's answer, I did an implementation in my project of a preprocessor macro that conditionally changed my properties from weak -> assign. Your mileage may vary:
#if __has_feature(objc_arc)
#define BBE_WEAK_PROPERTY(type, name) @property (weak, nonatomic) type name
#else
#define BBE_WEAK_PROPERTY(type, name) @property (assign, nonatomic) type name
#endif
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我们一直在努力解决同样的问题,它确实与 ARC 和对 NSProxy 派生对象的弱引用之间的不兼容有关。我建议使用预处理器指令有条件地编译弱委托引用以在测试套件中分配,以便您可以通过 OCMock 测试它们。
We've been struggling with this same issue and it does indeed have to do with an incompatibility between ARC and weak references to NSProxy derived objects. I would recommend using a pre-processor directive to conditionally compile your weak delegate references to assign within the test suite so you can test them via OCMock.
我找到了与条件宏不同的解决方案,因为我正在测试无法更改代码的代码。
我编写了一个扩展 NSObject(而不是 NSProxy)的简单类,它将所有选择器调用转发到 OCMockProxy。
CCWeakMockProxy.h:
CCWeakMockProxy.m:
只需像使用常规 OCMockObject 一样使用生成的对象即可!
I found a different solution than a conditional macro since I was testing code that I could not change the code for.
I wrote a simple class that extends NSObject, not NSProxy, that forwards all selector invocations on to the OCMockProxy.
CCWeakMockProxy.h:
CCWeakMockProxy.m:
Just use the resulting object as you would a regular OCMockObject!
当然。它会变成 nil,因为在分配
child.parent
之后,你的代理对象本身就会被你的测试释放(因为它不再被引用),这会导致对 nil 的弱引用出去。因此,解决方案是在测试期间保持代理对象处于活动状态。 插入对 的调用来简单地完成此操作您可以通过在方法末尾 。该函数调用不执行任何操作(
-self
仅返回self
),但它将确保 ARC 保持对象处于活动状态。另一种方法是将
aParent
声明更改为__autoreleasing
,这使得它的行为更像 MRR,因为 ARC 只会在该插槽中留下自动释放的引用,而不是显式地保留当变量超出范围时释放对象。您可以这样做,也就是说,第一个解决方案可能更干净,因为您在测试期间明确地保持对象处于活动状态。
Sure. It's going
nil
because immediately after assigningchild.parent
, your proxy object itself is released by your test (since it's no longer referenced), and this causes the weak reference to nil out. So the solution is to keep your proxy object alive during the test. You can do this trivially by inserting a call toat the end of your method. That function call does nothing (
-self
just returnsself
), but it will ensure that ARC keeps the object alive.An alternative would be to change your declaration of
aParent
to be__autoreleasing
, which makes it behave more like MRR in that ARC will just leave an autoreleased reference in that slot instead of explicitly releasing the object when the variable goes out of scope. You can do that withThat said, the first solution is probably cleaner, because you're explicitly keeping the object alive during the test.