首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使ARC下的OCMock停止使用弱属性来命名NSProxy子类集?

如何使ARC下的OCMock停止使用弱属性来命名NSProxy子类集?
EN

Stack Overflow用户
提问于 2012-02-01 22:47:45
回答 3查看 2K关注 0票数 10

ARC下,我有一个对象,Child,它有一个weak属性parent。我试图为Child编写一些测试,并使用OCMock来模拟它的parent属性。

在ARC下,使用合成的弱属性设置NSProxy子类不会设置属性.设置弱属性后的行,检查它就会发现它已经是nil了。下面是具体的例子:

代码语言:javascript
复制
@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属性来引用Parent,但是当我完成它(就像穴居人一样)时,我就不得不将parent导出出来,这正是weak应该避免的事情。

对于如何在不改变我的应用程序代码的情况下通过测试,有什么建议吗?

编辑:这似乎与OCMockObject是一个NSProxy有关,如果将aParent作为NSObject的一个实例,child.parent弱引用“持有”一个非零值。仍然在寻找一种方法,使这个测试通过而不改变应用程序代码。

编辑2:在接受Blake的回答后,我在我的项目中实现了一个预处理宏,该宏有条件地从弱->分配中更改了我的属性。你的里程可能不同:

代码语言:javascript
复制
#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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-12 18:12:19

我们一直在努力解决同样的问题,这确实与ARC与NSProxy派生对象的弱引用之间的不兼容性有关。我建议使用预处理器指令有条件地编译弱委托引用,以便在测试套件中分配,这样您就可以通过OCMock对它们进行测试。

票数 9
EN

Stack Overflow用户

发布于 2012-07-27 18:59:05

我找到了与条件宏不同的解决方案,因为我正在测试无法更改代码的代码。

我编写了一个扩展NSObject (而不是NSProxy )的简单类,它将所有选择器调用转发到OCMockProxy。

CCWeakMockProxy.h:

代码语言:javascript
复制
#import <Foundation/Foundation.h>

/**
 * This class is a hack around the fact that ARC weak references are immediately nil'd if the referent is an NSProxy
 * See: http://stackoverflow.com/questions/9104544/how-can-i-get-ocmock-under-arc-to-stop-nilling-an-nsproxy-subclass-set-using-a-w
 */
@interface CCWeakMockProxy : NSObject

@property (strong, nonatomic) id mock;

- (id)initWithMock:(id)mockObj;

+ (id)mockForClass:(Class)aClass;
+ (id)mockForProtocol:(Protocol *)aProtocol;
+ (id)niceMockForClass:(Class)aClass;
+ (id)niceMockForProtocol:(Protocol *)aProtocol;
+ (id)observerMock;
+ (id)partialMockForObject:(NSObject *)anObject;

@end

CCWeakMockProxy.m:

代码语言:javascript
复制
#import "CCWeakMockProxy.h"
#import <OCMock/OCMock.h>


#pragma mark Implementation
@implementation CCWeakMockProxy

#pragma mark Properties
@synthesize mock;

#pragma mark Memory Management
- (id)initWithMock:(id)mockObj {
    if (self = [super init]) {
        self.mock = mockObj;
    }
    return self;
}

#pragma mark NSObject
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return self.mock;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [self.mock respondsToSelector:aSelector];
}

#pragma mark Public Methods
+ (id)mockForClass:(Class)aClass {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject mockForClass:aClass]];
}

+ (id)mockForProtocol:(Protocol *)aProtocol {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject mockForProtocol:aProtocol]];
}

+ (id)niceMockForClass:(Class)aClass {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject niceMockForClass:aClass]];
}

+ (id)niceMockForProtocol:(Protocol *)aProtocol {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject niceMockForProtocol:aProtocol]];
}

+ (id)observerMock {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject observerMock]];
}

+ (id)partialMockForObject:(NSObject *)anObject {
    return [[CCWeakMockProxy alloc] initWithMock:[OCMockObject partialMockForObject:anObject]];
}

@end

只需像使用常规OCMockObject一样使用结果对象即可!

票数 6
EN

Stack Overflow用户

发布于 2012-02-01 23:13:54

好的。它之所以成为nil,是因为在分配child.parent之后,代理对象本身就会被测试释放(因为它不再被引用),这会导致弱引用为零。因此,解决方案是在测试期间保持代理对象的活动。您可以将调用插入到

代码语言:javascript
复制
[aParent self];

在你的方法的最后。该函数调用什么也不做(-self只返回self),但它将确保self保持对象的活力。

另一种方法是将aParent的声明更改为__autoreleasing,这使得它的行为更像MRR,因为ARC只会在该槽中保留一个自动释放的引用,而不是在变量超出作用域时显式释放对象。你可以用

代码语言:javascript
复制
__autoreleasing OCMockObject *aParent = ...

尽管如此,第一个解决方案可能更干净,因为在测试期间,您显式地保持了对象的活动状态。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9104544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档