diff --git a/Source/OCMock/NSInvocation+OCMAdditions.m b/Source/OCMock/NSInvocation+OCMAdditions.m index 6561dd9a..9626550d 100644 --- a/Source/OCMock/NSInvocation+OCMAdditions.m +++ b/Source/OCMock/NSInvocation+OCMAdditions.m @@ -20,7 +20,7 @@ #import "NSInvocation+OCMAdditions.h" #import "OCMFunctionsPrivate.h" #import "NSMethodSignature+OCMAdditions.h" -#import "NSObject+OCMAdditions.h" + #if (TARGET_OS_OSX && (!defined(__MAC_10_10) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10)) || \ (TARGET_OS_IPHONE && (!defined(__IPHONE_8_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0)) @@ -30,6 +30,7 @@ static BOOL OCMObjectIsClass(id object) { #define object_isClass OCMObjectIsClass #endif + @implementation NSInvocation(OCMAdditions) + (NSInvocation *)invocationForBlock:(id)block withArguments:(NSArray *)arguments @@ -65,7 +66,7 @@ - (void)retainObjectArgumentsExcludingObject:(id)objectToExclude NSMutableArray *retainedArguments = [[NSMutableArray alloc] init]; id target = [self target]; - if((target != nil) && (target != objectToExclude) && !object_isClass(target) && ![target _isDeallocating]) + if((target != nil) && (target != objectToExclude) && !object_isClass(target) && !OCMIsDeallocating(target)) { // Bad things will happen if the target is a block since it's not being // copied. There isn't a very good way to tell if an invocation's target @@ -83,7 +84,7 @@ - (void)retainObjectArgumentsExcludingObject:(id)objectToExclude { id argument; [self getArgument:&argument atIndex:index]; - if((argument != nil) && (argument != objectToExclude) && ![argument _isDeallocating]) + if((argument != nil) && (argument != objectToExclude) && !OCMIsDeallocating(argument)) { if(OCMIsBlockType(argumentType)) { @@ -105,7 +106,7 @@ - (void)retainObjectArgumentsExcludingObject:(id)objectToExclude { id returnValue; [self getReturnValue:&returnValue]; - if((returnValue != nil) && (returnValue != objectToExclude) && ![returnValue _isDeallocating]) + if((returnValue != nil) && (returnValue != objectToExclude) && !OCMIsDeallocating(returnValue)) { if(OCMIsBlockType(returnType)) { diff --git a/Source/OCMock/NSObject+OCMAdditions.h b/Source/OCMock/NSObject+OCMAdditions.h index 32badc1c..86d7063e 100644 --- a/Source/OCMock/NSObject+OCMAdditions.h +++ b/Source/OCMock/NSObject+OCMAdditions.h @@ -22,9 +22,3 @@ + (void)enumerateMethodsInClass:(Class)aClass usingBlock:(void (^)(Class cls, SEL sel))aBlock; @end - - -@interface NSObject (Internals) -// Return YES if an object is in the process of being deallocated. -- (BOOL)_isDeallocating; -@end diff --git a/Source/OCMock/OCMFunctions.m b/Source/OCMock/OCMFunctions.m index c2159b3d..104251f5 100644 --- a/Source/OCMock/OCMFunctions.m +++ b/Source/OCMock/OCMFunctions.m @@ -32,6 +32,10 @@ - (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *) - (void)failWithException:(NSException *)exception; @end +// From objc/runtime/objc-internal.h +// Only available on macOS 10.11/iOS 9. +extern id objc_initWeakOrNil(id *location, id newObj) __attribute__((weak_import)); +extern void objc_destroyWeak(id _Nullable * _Nonnull location) __attribute__((weak_import)); #pragma mark Functions related to ObjC type system @@ -406,3 +410,20 @@ void OCMReportFailure(OCMLocation *loc, NSString *description) } } + +BOOL OCMIsDeallocating(id anObject) +{ + if (!objc_initWeakOrNil) + { + // Pre iOS9/macOS10.11 we just assume all is well since we can't check. + return NO; + } + id deallocatingObject = nil; + BOOL isDeallocating = objc_initWeakOrNil(&deallocatingObject, anObject) == nil; + if (deallocatingObject) + { + objc_destroyWeak(&deallocatingObject); + } + return isDeallocating; +} + diff --git a/Source/OCMock/OCMFunctionsPrivate.h b/Source/OCMock/OCMFunctionsPrivate.h index cee4558c..ee503a07 100644 --- a/Source/OCMock/OCMFunctionsPrivate.h +++ b/Source/OCMock/OCMFunctionsPrivate.h @@ -40,3 +40,5 @@ void OCMSetAssociatedMockForObject(OCClassMockObject *mock, id anObject); OCPartialMockObject *OCMGetAssociatedMockForObject(id anObject); void OCMReportFailure(OCMLocation *loc, NSString *description); + +BOOL OCMIsDeallocating(id anObject); diff --git a/Source/OCMock/OCMStubRecorder.m b/Source/OCMock/OCMStubRecorder.m index 3e7ed48e..7ff4efb9 100644 --- a/Source/OCMock/OCMStubRecorder.m +++ b/Source/OCMock/OCMStubRecorder.m @@ -25,7 +25,7 @@ #import "OCMRealObjectForwarder.h" #import "OCMFunctions.h" #import "OCMInvocationStub.h" -#import "NSObject+OCMAdditions.h" +#import "OCMFunctionsPrivate.h" @implementation OCMStubRecorder @@ -53,7 +53,7 @@ - (OCMInvocationStub *)stub - (id)andReturn:(id)anObject { id action; - if(anObject == mockObject || [anObject _isDeallocating]) + if(anObject == mockObject || OCMIsDeallocating(anObject)) { action = [[[OCMNonRetainingObjectReturnValueProvider alloc] initWithValue:anObject] autorelease]; } diff --git a/Source/OCMockTests/OCMockObjectPartialMocksTests.m b/Source/OCMockTests/OCMockObjectPartialMocksTests.m index 9d941e9a..6c8d6c61 100644 --- a/Source/OCMockTests/OCMockObjectPartialMocksTests.m +++ b/Source/OCMockTests/OCMockObjectPartialMocksTests.m @@ -608,4 +608,5 @@ - (void)testMethodSwizzlingWorksForVoidReturns XCTAssertNoThrow([foo method1], @"Should have worked."); } + @end