How can I get OCMock under ARC to stop nilling an NSProxy subclass set using a weak property?

前端 未结 3 1183
夕颜
夕颜 2020-12-31 12:01

Under ARC, I have an object, Child that has a weak property, parent. I\'m trying to write some tests for Child

相关标签:
3条回答
  • 2020-12-31 12:24

    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.

    0 讨论(0)
  • 2020-12-31 12:29

    Sure. It's going nil because immediately after assigning child.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 to

    [aParent self];
    

    at the end of your method. That function call does nothing (-self just returns self), 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 with

    __autoreleasing OCMockObject *aParent = ...
    

    That said, the first solution is probably cleaner, because you're explicitly keeping the object alive during the test.

    0 讨论(0)
  • 2020-12-31 12:33

    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:

    #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:

    #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
    

    Just use the resulting object as you would a regular OCMockObject!

    0 讨论(0)
提交回复
热议问题