Skip to content

Commit 90693d3

Browse files
committed
Merge branch 'dmaclach-initLeak'
2 parents 4343dc4 + 0e5a5c3 commit 90693d3

19 files changed

+418
-148
lines changed

Source/OCMock/NSInvocation+OCMAdditions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@
4646
- (NSString *)cStringDescriptionAtIndex:(NSInteger)anInt;
4747
- (NSString *)selectorDescriptionAtIndex:(NSInteger)anInt;
4848

49+
- (BOOL)methodIsInInitFamily;
50+
- (BOOL)methodIsInAllocFamily;
51+
- (BOOL)methodIsInCopyFamily;
52+
- (BOOL)methodIsInMutableCopyFamily;
53+
- (BOOL)methodIsInNewFamily;
54+
55+
4956
@end

Source/OCMock/NSInvocation+OCMAdditions.m

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,59 @@ - (NSString *)selectorDescriptionAtIndex:(NSInteger)anInt
528528
return [NSString stringWithFormat:@"@selector(%@)", NSStringFromSelector(selectorValue)];
529529
}
530530

531+
532+
- (BOOL)isMethodFamily:(NSString *)family
533+
{
534+
// Definitions here: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families
535+
536+
NSMethodSignature *signature = [self methodSignature];
537+
if(OCMIsObjectType(signature.methodReturnType) == NO)
538+
{
539+
return NO;
540+
}
541+
542+
NSString *selString = NSStringFromSelector([self selector]);
543+
NSRange underscoreRange = [selString rangeOfString:@"^_*" options:NSRegularExpressionSearch];
544+
selString = [selString substringFromIndex:NSMaxRange(underscoreRange)];
545+
546+
if([selString hasPrefix:family] == NO)
547+
{
548+
return NO;
549+
}
550+
NSUInteger familyLength = [family length];
551+
if(([selString length] > familyLength) &&
552+
([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[selString characterAtIndex:familyLength]]))
553+
{
554+
return NO;
555+
}
556+
return YES;
557+
}
558+
559+
560+
- (BOOL)methodIsInInitFamily
561+
{
562+
return [self isMethodFamily:@"init"];
563+
}
564+
565+
- (BOOL)methodIsInAllocFamily
566+
{
567+
return [self isMethodFamily:@"alloc"];
568+
}
569+
570+
- (BOOL)methodIsInCopyFamily
571+
{
572+
return [self isMethodFamily:@"copy"];
573+
}
574+
575+
- (BOOL)methodIsInMutableCopyFamily
576+
{
577+
return [self isMethodFamily:@"mutableCopy"];
578+
}
579+
580+
- (BOOL)methodIsInNewFamily
581+
{
582+
return [self isMethodFamily:@"new"];
583+
}
584+
585+
531586
@end

Source/OCMock/OCMFunctionsPrivate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ void OCMSetAssociatedMockForObject(OCClassMockObject *mock, id anObject);
4040
OCPartialMockObject *OCMGetAssociatedMockForObject(id anObject);
4141

4242
void OCMReportFailure(OCMLocation *loc, NSString *description);
43+

Source/OCMock/OCMIndirectReturnValueProvider.m

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
* under the License.
1515
*/
1616

17-
#import "NSMethodSignature+OCMAdditions.h"
1817
#import "OCMIndirectReturnValueProvider.h"
19-
#import "NSInvocation+OCMAdditions.h"
2018

2119

2220
@implementation OCMIndirectReturnValueProvider

Source/OCMock/OCMInvocationMatcher.m

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
#import "OCMPassByRefSetter.h"
2121
#import "NSInvocation+OCMAdditions.h"
2222
#import "OCMInvocationMatcher.h"
23-
#import "OCClassMockObject.h"
2423
#import "OCMFunctionsPrivate.h"
25-
#import "OCMBlockArgCaller.h"
2624

2725

2826
@interface NSObject(HCMatcherDummy)

Source/OCMock/OCMInvocationStub.m

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
*/
1616

1717
#import "OCMInvocationStub.h"
18-
#import "OCMFunctionsPrivate.h"
1918
#import "OCMArg.h"
2019
#import "OCMArgAction.h"
2120
#import "NSInvocation+OCMAdditions.h"
2221

22+
#define UNSET_RETURN_VALUE_MARKER ((id)0x01234567)
23+
2324
@implementation OCMInvocationStub
2425

2526
- (id)init
@@ -48,6 +49,29 @@ - (NSArray *)invocationActions
4849

4950

5051
- (void)handleInvocation:(NSInvocation *)anInvocation
52+
{
53+
[self invokeArgActionsForInvocation:anInvocation];
54+
55+
if([anInvocation methodIsInInitFamily])
56+
{
57+
id returnVal = UNSET_RETURN_VALUE_MARKER;
58+
[anInvocation setReturnValue:&returnVal];
59+
60+
[self invokeActionsForInvocation:anInvocation];
61+
62+
[anInvocation getReturnValue:&returnVal];
63+
if(returnVal == UNSET_RETURN_VALUE_MARKER)
64+
{
65+
[NSException raise:NSInvalidArgumentException format:@"%@ was stubbed but no return value set. A return value is required for an init method. If you intended to return nil, make this explicit with .andReturn(nil)", NSStringFromSelector([anInvocation selector])];
66+
}
67+
}
68+
else
69+
{
70+
[self invokeActionsForInvocation:anInvocation];
71+
}
72+
}
73+
74+
- (void)invokeArgActionsForInvocation:(NSInvocation *)anInvocation
5175
{
5276
NSMethodSignature *signature = [recordedInvocation methodSignature];
5377
NSUInteger n = [signature numberOfArguments];
@@ -62,13 +86,15 @@ - (void)handleInvocation:(NSInvocation *)anInvocation
6286
if([recordedArg isKindOfClass:[NSValue class]])
6387
recordedArg = [OCMArg resolveSpecialValues:recordedArg];
6488

65-
if(![recordedArg isKindOfClass:[OCMArgAction class]])
66-
continue;
67-
68-
[recordedArg handleArgument:passedArg];
89+
if([recordedArg isKindOfClass:[OCMArgAction class]])
90+
[recordedArg handleArgument:passedArg];
6991
}
92+
}
7093

94+
- (void)invokeActionsForInvocation:(NSInvocation *)anInvocation
95+
{
7196
[invocationActions makeObjectsPerformSelector:@selector(handleInvocation:) withObject:anInvocation];
7297
}
7398

99+
74100
@end

Source/OCMock/OCMMacroState.m

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616

1717
#import "OCMMacroState.h"
1818
#import "OCMStubRecorder.h"
19-
#import "OCMockObject.h"
2019
#import "OCMExpectationRecorder.h"
21-
#import "OCMVerifier.h"
22-
#import "OCMInvocationMatcher.h"
2320

2421

2522
@implementation OCMMacroState
@@ -71,14 +68,17 @@ + (OCMStubRecorder *)endExpectMacro
7168
+ (void)beginRejectMacro
7269
{
7370
OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease];
74-
[recorder never];
7571
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
7672
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
7773
[macroState release];
7874
}
7975

8076
+ (OCMStubRecorder *)endRejectMacro
8177
{
78+
OCMMacroState *globalState = [NSThread currentThread].threadDictionary[OCMGlobalStateKey];
79+
// Calling never after the invocation to avoid running afoul of ARC's expectations on
80+
// return values from init methods.
81+
[(OCMExpectationRecorder *)[globalState recorder] never];
8282
return [self endStubMacro];
8383
}
8484

Source/OCMock/OCMNonRetainingObjectReturnValueProvider.m

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#import "OCMFunctions.h"
1818
#import "OCMNonRetainingObjectReturnValueProvider.h"
19-
19+
#import "NSInvocation+OCMAdditions.h"
2020

2121
@implementation OCMNonRetainingObjectReturnValueProvider
2222

@@ -33,12 +33,19 @@ - (void)handleInvocation:(NSInvocation *)anInvocation
3333
{
3434
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Expected invocation with object return type. Did you mean to use andReturnValue: instead?" userInfo:nil];
3535
}
36-
NSString *sel = NSStringFromSelector([anInvocation selector]);
37-
if([sel hasPrefix:@"alloc"] || [sel hasPrefix:@"new"] || [sel hasPrefix:@"copy"] || [sel hasPrefix:@"mutableCopy"])
36+
37+
if([anInvocation methodIsInAllocFamily] || [anInvocation methodIsInNewFamily] ||
38+
[anInvocation methodIsInCopyFamily] || [anInvocation methodIsInMutableCopyFamily])
3839
{
3940
// methods that "create" an object return it with an extra retain count
4041
[returnValue retain];
4142
}
43+
else if([anInvocation methodIsInInitFamily])
44+
{
45+
// init family methods "consume" self and retain their return value. Do the retain first in case the return value and self are the same.
46+
[returnValue retain];
47+
[[anInvocation target] release];
48+
}
4249
[anInvocation setReturnValue:&returnValue];
4350
}
4451
@end

Source/OCMock/OCMQuantifier.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414
* under the License.
1515
*/
1616

17-
#import <Foundation/Foundation.h>
17+
#import "OCMQuantifier.h"
1818
#import "OCMMacroState.h"
1919
#import "OCMVerifier.h"
20-
#import "OCMQuantifier.h"
2120

2221

2322
@interface OCMExactCountQuantifier : OCMQuantifier

Source/OCMock/OCMRecorder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,23 @@
2525
OCMockObject *mockObject;
2626
OCMInvocationMatcher *invocationMatcher;
2727
BOOL wasUsed;
28+
BOOL shouldReturnMockFromInit;
2829
}
2930

31+
3032
- (instancetype)init;
3133
- (instancetype)initWithMockObject:(OCMockObject *)aMockObject;
3234

3335
- (void)setMockObject:(OCMockObject *)aMockObject;
36+
- (void)setShouldReturnMockFromInit:(BOOL)flag;
3437

3538
- (OCMInvocationMatcher *)invocationMatcher;
3639
- (BOOL)wasUsed;
3740

3841
- (id)classMethod;
3942
- (id)ignoringNonObjectArgs;
4043

44+
4145
@end
4246

4347
@interface OCMRecorder (Properties)

0 commit comments

Comments
 (0)