Objective c 如何让OCMock在ARC下停止使用弱属性nilling NSProxy子类集?

Objective c 如何让OCMock在ARC下停止使用弱属性nilling NSProxy子类集?,objective-c,unit-testing,automatic-ref-counting,ocmock,nsproxy,Objective C,Unit Testing,Automatic Ref Counting,Ocmock,Nsproxy,在ARC下,我有一个对象,Child,它有一个weak属性,parent。我试图为子对象编写一些测试,并使用OCMock模拟其父对象属性 在ARC下,使用合成的弱属性设置器设置NSProxy子类不会设置属性。。。设置弱属性后的行,检查它会显示它已经是nil。下面是一个具体的例子: @interface Child : NSObject @property (nonatomic, weak) id <ParentInterface>parent; @end @implementati

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

在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.
}

当然。它将变为
nil
,因为在分配
child.parent
之后,代理对象本身会被测试释放(因为它不再被引用),这会导致弱引用变为nil out。因此,解决方案是在测试期间保持代理对象的活动状态。您可以通过插入对的调用来实现这一点

[aParent self];
在方法的末尾。该函数调用不执行任何操作(
-self
只返回
self
),但它将确保ARC使对象保持活动状态

另一种方法是将
aParent
的声明更改为
\uu autoreleasing
,这使得它的行为更像MRR,因为ARC只会在该插槽中保留一个autoreleased引用,而不是在变量超出范围时显式释放对象。你可以用它来做

__autoreleasing OCMockObject *aParent = ...

这就是说,第一个解决方案可能更干净,因为您在测试期间显式地保持对象的活动状态。

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

我发现了一种不同于条件宏的解决方案,因为我正在测试无法更改代码的代码

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

CCWeakMockProxy.h:

#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
#导入
/**
*如果引用对象是NSProxy,那么弧弱引用将立即为零,这个类是对这一事实的一种破解
*见:http://stackoverflow.com/questions/9104544/how-can-i-get-ocmock-under-arc-to-stop-nilling-an-nsproxy-subclass-set-using-a-w
*/
@接口CCWeakMockProxy:NSObject
@属性(强,非原子)id mock;
-(id)initWithMock:(id)mockObj;
+(id)mockForClass:(Class)aClass;
+(id)协议:(协议*)协议;
+(id)类:(类)aClass;
+(id)协议:(协议*)协议;
+(id)观察者模拟;
+(id)partialMockForObject:(NSObject*)一个对象;
@结束
CCWeakMockProxy.m:

#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
#导入“CCWeakMockProxy.h”
#进口
#pragma标记实现
@CCWeakMockProxy的实现
#pragma标记属性
@综合模拟;
#pragma标记内存管理
-(id)initWithMock:(id)mockObj{
if(self=[super init]){
self.mock=mockObj;
}
回归自我;
}
#pragma标记对象
-(id)转发目标供选择人:(SEL)选择人{
回归自我嘲弄;
}
-(BOOL)响应选择器:(SEL)选择器{
返回[self.mock respondsToSelector:aSelector];
}
#pragma标记公共方法
+(id)mockForClass:(Class)aClass{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject mockForClass:aClass]];
}
+(id)mockForProtocol:(协议*)aProtocol{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject mockForProtocol:aProtocol]];
}
+(id)niceMockForClass:(Class)aClass{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject niceMockForClass:aClass]];
}
+(id)协议:(协议*)协议{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject niceMockForProtocol:aProtocol]];
}
+(id)观察者模拟{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject observerMock]];
}
+(id)partialMockForObject:(NSObject*)一个对象{
返回[[CCWeakMockProxy alloc]initWithMock:[OCMockObject partialMockForObject:anObject]];
}
@结束

只需像使用普通对象一样使用结果对象

我在测试的底部添加了您建议的参考,并已验证它不起作用
parent
在测试底部仍然有效,但是
child。parent
总是
nil
@Prairiedogg:那么,您的测试是否按预期工作?这或OCMock与弱引用不兼容(如果它覆盖保留/释放,则可能发生这种情况)。添加
\uu自动释放
似乎也不起作用。我可以验证测试范围中对
aParent
的引用在测试方法的最后一行一直有效,但是合成的
child
属性从未设置过-它总是
nil
@Prairiedogg:尝试将其更改为
assign
。如果这样做有效,则意味着
OCMockObject
根本不支持弱引用。如果这也失败了,那么你肯定有问题。你确定
child
本身是非零的吗?当然-我已经验证了
assign
是否有效(在原始问题中提到)。我确实验证了
child
不是nil。我还验证了将
aParent
更改为
NSObject
的实例会使
child.parent
“保持”正确。查看此提交消息:看起来OCMock团队正在研究相同的问题。
#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