OCMock method name collision - ios

i'm a new user of OCMock, so maybe i'm just missing something simple here. this code does not compile:
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[[mockSession expect] addOutput:[OCMArg anyPointer]];
the error is
Multiple methods named 'addOutput:' found with mismatched result, parameter type or attributes
the signature of the method addOutput on AVCaptureSession is as follows
- (void)addOutput:(AVCaptureOutput *)output
as far as i can tell, the problem is that the method addOutput exists on both the AVCaptureSession and AVAssetReader classes. the method signature for addOutput on AVAssetReader is as follows.
- (void)addOutput:(AVAssetReaderOutput *)output
apparently the compiler thinks my mockSession is an AVAssetReader, but i don't know why it chooses that class instead of AVCaptureSession. if i expect a different method on AVCaptureSession that does not exist on AVAssetReader, then it compiles. i have tried the following without success. it compiles, but crashes.
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[(AVCaptureSession*)[mockSession expect] addOutput:[OCMArg anyPointer]];
this code also does not compile, with the same error as the previous one
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[[mockSession expect] addOutput:output];
any guidance here?

In cases where your variable is an "id" but a method is declared with different signatures in different classes you should help the compiler by casting the object to the correct type, e.g.
[((AVCaptureSession *)[mockSession expect]) addOutput:[OCMArg any]];
In either case, if the argument is an object, as it seems in your case, you should use any and not anyPointer. But you figured that one out already. ;-)

ok, i think it figured it out. as i suspected, it was a simple noob mistake. changing [OCMArg anyPointer] to [OCMArg any] makes the following work:
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[(AVCaptureSession*)[mockSession expect] addOutput:[OCMArg any]];

You need to inform the compiler that it's fine
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-multiple-method-names"
#pragma clang diagnostic ignored "-Wstrict-selector-match"
OCMStub([globalContextMock sharedContext]).andReturn(context);
#pragma clang diagnostic pop

Related

How to solve "PerformSelector may cause leak because its selector is unknown" [duplicate]

I'm getting the following warning by the ARC compiler:
"performSelector may cause a leak because its selector is unknown".
Here's what I'm doing:
[_controller performSelector:NSSelectorFromString(#"someMethod")];
Why do I get this warning? I understand the compiler can't check if the selector exists or not, but why would that cause a leak? And how can I change my code so that I don't get this warning anymore?
Solution
The compiler is warning about this for a reason. It's very rare that this warning should simply be ignored, and it's easy to work around. Here's how:
if (!_controller) { return; }
SEL selector = NSSelectorFromString(#"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);
Or more tersely (though hard to read & without the guard):
SEL selector = NSSelectorFromString(#"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);
Explanation
What's going on here is you're asking the controller for the C function pointer for the method corresponding to the controller. All NSObjects respond to methodForSelector:, but you can also use class_getMethodImplementation in the Objective-C runtime (useful if you only have a protocol reference, like id<SomeProto>). These function pointers are called IMPs, and are simple typedefed function pointers (id (*IMP)(id, SEL, ...))1. This may be close to the actual method signature of the method, but will not always match exactly.
Once you have the IMP, you need to cast it to a function pointer that includes all of the details that ARC needs (including the two implicit hidden arguments self and _cmd of every Objective-C method call). This is handled in the third line (the (void *) on the right hand side simply tells the compiler that you know what you're doing and not to generate a warning since the pointer types don't match).
Finally, you call the function pointer2.
Complex Example
When the selector takes arguments or returns a value, you'll have to change things a bit:
SEL selector = NSSelectorFromString(#"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
func(_controller, selector, someRect, someView) : CGRectZero;
Reasoning for Warning
The reason for this warning is that with ARC, the runtime needs to know what to do with the result of the method you're calling. The result could be anything: void, int, char, NSString *, id, etc. ARC normally gets this information from the header of the object type you're working with.3
There are really only 4 things that ARC would consider for the return value:4
Ignore non-object types (void, int, etc)
Retain object value, then release when it is no longer used (standard assumption)
Release new object values when no longer used (methods in the init/ copy family or attributed with ns_returns_retained)
Do nothing & assume returned object value will be valid in local scope (until inner most release pool is drained, attributed with ns_returns_autoreleased)
The call to methodForSelector: assumes that the return value of the method it's calling is an object, but does not retain/release it. So you could end up creating a leak if your object is supposed to be released as in #3 above (that is, the method you're calling returns a new object).
For selectors you're trying to call that return void or other non-objects, you could enable compiler features to ignore the warning, but it may be dangerous. I've seen Clang go through a few iterations of how it handles return values that aren't assigned to local variables. There's no reason that with ARC enabled that it can't retain and release the object value that's returned from methodForSelector: even though you don't want to use it. From the compiler's perspective, it is an object after all. That means that if the method you're calling, someMethod, is returning a non object (including void), you could end up with a garbage pointer value being retained/released and crash.
Additional Arguments
One consideration is that this is the same warning will occur with performSelector:withObject: and you could run into similar problems with not declaring how that method consumes parameters. ARC allows for declaring consumed parameters, and if the method consumes the parameter, you'll probably eventually send a message to a zombie and crash. There are ways to work around this with bridged casting, but really it'd be better to simply use the IMP and function pointer methodology above. Since consumed parameters are rarely an issue, this isn't likely to come up.
Static Selectors
Interestingly, the compiler will not complain about selectors declared statically:
[_controller performSelector:#selector(someMethod)];
The reason for this is because the compiler actually is able to record all of the information about the selector and the object during compilation. It doesn't need to make any assumptions about anything. (I checked this a year a so ago by looking at the source, but don't have a reference right now.)
Suppression
In trying to think of a situation where suppression of this warning would be necessary and good code design, I'm coming up blank. Someone please share if they have had an experience where silencing this warning was necessary (and the above doesn't handle things properly).
More
It's possible to build up an NSMethodInvocation to handle this as well, but doing so requires a lot more typing and is also slower, so there's little reason to do it.
History
When the performSelector: family of methods was first added to Objective-C, ARC did not exist. While creating ARC, Apple decided that a warning should be generated for these methods as a way of guiding developers toward using other means to explicitly define how memory should be handled when sending arbitrary messages via a named selector. In Objective-C, developers are able to do this by using C style casts on raw function pointers.
With the introduction of Swift, Apple has documented the performSelector: family of methods as "inherently unsafe" and they are not available to Swift.
Over time, we have seen this progression:
Early versions of Objective-C allow performSelector: (manual memory management)
Objective-C with ARC warns for use of performSelector:
Swift does not have access to performSelector: and documents these methods as "inherently unsafe"
The idea of sending messages based on a named selector is not, however, an "inherently unsafe" feature. This idea has been used successfully for a long time in Objective-C as well as many other programming languages.
1 All Objective-C methods have two hidden arguments, self and _cmd that are implicitly added when you call a method.
2 Calling a NULL function is not safe in C. The guard used to check for the presence of the controller ensures that we have an object. We therefore know we'll get an IMP from methodForSelector: (though it may be _objc_msgForward, entry into the message forwarding system). Basically, with the guard in place, we know we have a function to call.
3 Actually, it's possible for it to get the wrong info if declare you objects as id and you're not importing all headers. You could end up with crashes in code that the compiler thinks is fine. This is very rare, but could happen. Usually you'll just get a warning that it doesn't know which of two method signatures to choose from.
4 See the ARC reference on retained return values and unretained return values for more details.
In the LLVM 3.0 compiler in Xcode 4.2 you can suppress the warning as follows:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.ticketTarget performSelector: self.ticketAction withObject: self];
#pragma clang diagnostic pop
If you're getting the error in several places, and want to use the C macro system to hide the pragmas, you can define a macro to make it easier to suppress the warning:
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
You can use the macro like this:
SuppressPerformSelectorLeakWarning(
[_target performSelector:_action withObject:self]
);
If you need the result of the performed message, you can do this:
id result;
SuppressPerformSelectorLeakWarning(
result = [_target performSelector:_action withObject:self]
);
My guess about this is this: since the selector is unknown to the compiler, ARC cannot enforce proper memory management.
In fact, there are times when memory management is tied to the name of the method by a specific convention. Specifically, I am thinking of convenience constructors versus make methods; the former return by convention an autoreleased object; the latter a retained object. The convention is based on the names of the selector, so if the compiler does not know the selector, then it cannot enforce the proper memory management rule.
If this is correct, I think that you can safely use your code, provided you make sure that everything is ok as to memory management (e.g., that your methods do not return objects that they allocate).
In your project Build Settings, under Other Warning Flags (WARNING_CFLAGS), add
-Wno-arc-performSelector-leaks
Now just make sure that the selector you are calling does not cause your object to be retained or copied.
As a workaround until the compiler allows overriding the warning, you can use the runtime.
You need header:
#import <objc/message.h>
Then try below:
// For strict compilers.
((id(*)(id,SEL))objc_msgSend)(_controller, sel_getUid("someMethod"));
OR
// Old answer's code:
objc_msgSend(_controller, NSSelectorFromString(#"someMethod"));
Instead of:
[_controller performSelector:NSSelectorFromString(#"someMethod")];
To ignore the error only in the file with the perform selector, add a #pragma as follows:
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
This would ignore the warning on this line, but still allow it throughout the rest of your project.
Strange but true: if acceptable (i.e. result is void and you don't mind letting the runloop cycle once), add a delay, even if this is zero:
[_controller performSelector:NSSelectorFromString(#"someMethod")
withObject:nil
afterDelay:0];
This removes the warning, presumably because it reassures the compiler that no object can be returned and somehow mismanaged.
Here is an updated macro based on the answer given above. This one should allow you to wrap your code even with a return statement.
#define SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(code) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
code; \
_Pragma("clang diagnostic pop") \
SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(
return [_target performSelector:_action withObject:self]
);
This code doesn't involve compiler flags or direct runtime calls:
SEL selector = #selector(zeroArgumentMethod);
NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation invoke];
NSInvocation allows multiple arguments to be set so unlike performSelector this will work on any method.
Well, lots of answers here, but since this is a little different, combining a few answers I thought I'd put it in. I'm using an NSObject category which checks to make sure the selector returns void, and also suppresses the compiler warning.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Debug.h" // not given; just an assert
#interface NSObject (Extras)
// Enforce the rule that the selector used must return void.
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object;
- (void) performVoidReturnSelector:(SEL)aSelector;
#end
#implementation NSObject (Extras)
// Apparently the reason the regular performSelect gives a compile time warning is that the system doesn't know the return type. I'm going to (a) make sure that the return type is void, and (b) disable this warning
// See http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown
- (void) checkSelector:(SEL)aSelector {
// See http://stackoverflow.com/questions/14602854/objective-c-is-there-a-way-to-check-a-selector-return-value
Method m = class_getInstanceMethod([self class], aSelector);
char type[128];
method_getReturnType(m, type, sizeof(type));
NSString *message = [[NSString alloc] initWithFormat:#"NSObject+Extras.performVoidReturnSelector: %#.%# selector (type: %s)", [self class], NSStringFromSelector(aSelector), type];
NSLog(#"%#", message);
if (type[0] != 'v') {
message = [[NSString alloc] initWithFormat:#"%# was not void", message];
[Debug assertTrue:FALSE withMessage:message];
}
}
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Since the selector (aSelector) is returning void, it doesn't make sense to try to obtain the return result of performSelector. In fact, if we do, it crashes the app.
[self performSelector: aSelector withObject: object];
#pragma clang diagnostic pop
}
- (void) performVoidReturnSelector:(SEL)aSelector {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector: aSelector];
#pragma clang diagnostic pop
}
#end
For posterity's sake, I've decided to throw my hat into the ring :)
Recently I've been seeing more and more restructuring away from the target/selector paradigm, in favor of things such as protocols, blocks, etc. However, there is one drop-in replacement for performSelector that I've used a few times now:
[NSApp sendAction: NSSelectorFromString(#"someMethod") to: _controller from: nil];
These seem to be a clean, ARC-safe, and nearly identical replacement for performSelector without having to much about with objc_msgSend().
Though, I have no idea if there is an analog available on iOS.
Matt Galloway's answer on this thread explains the why:
Consider the following:
id anotherObject1 = [someObject performSelector:#selector(copy)];
id anotherObject2 = [someObject performSelector:#selector(giveMeAnotherNonRetainedObject)];
Now, how can ARC know that the first returns an object with a retain count of 1 but the second
returns an object which is autoreleased?
It seems that it is generally safe to suppress the warning if you are ignoring the return value. I'm not sure what the best practice is if you really need to get a retained object from performSelector -- other than "don't do that".
#c-road provides the right link with problem description here. Below you can see my example, when performSelector causes a memory leak.
#interface Dummy : NSObject <NSCopying>
#end
#implementation Dummy
- (id)copyWithZone:(NSZone *)zone {
return [[Dummy alloc] init];
}
- (id)clone {
return [[Dummy alloc] init];
}
#end
void CopyDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy copy];
}
void CloneDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy clone];
}
void CopyDummyWithLeak(Dummy *dummy, SEL copySelector) {
__unused Dummy *dummyClone = [dummy performSelector:copySelector];
}
void CloneDummyWithoutLeak(Dummy *dummy, SEL cloneSelector) {
__unused Dummy *dummyClone = [dummy performSelector:cloneSelector];
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
Dummy *dummy = [[Dummy alloc] init];
for (;;) { #autoreleasepool {
//CopyDummy(dummy);
//CloneDummy(dummy);
//CloneDummyWithoutLeak(dummy, #selector(clone));
CopyDummyWithLeak(dummy, #selector(copy));
[NSThread sleepForTimeInterval:1];
}}
}
return 0;
}
The only method, which causes memory leak in my example is CopyDummyWithLeak. The reason is that ARC doesn't know, that copySelector returns retained object.
If you'll run Memory Leak Tool you can see the following picture:
...and there are no memory leaks in any other case:
To make Scott Thompson's macro more generic:
// String expander
#define MY_STRX(X) #X
#define MY_STR(X) MY_STRX(X)
#define MYSilenceWarning(FLAG, MACRO) \
_Pragma("clang diagnostic push") \
_Pragma(MY_STR(clang diagnostic ignored MY_STR(FLAG))) \
MACRO \
_Pragma("clang diagnostic pop")
Then use it like this:
MYSilenceWarning(-Warc-performSelector-leaks,
[_target performSelector:_action withObject:self];
)
Do not suppress warnings!
There are no less than 12 alternative solutions to tinkering with the compiler.
While you are being clever at the time the first implementation, few engineer on Earth can follow your footsteps, and this code will eventually break.
Safe Routes:
All these solutions will work, with some degree of of variation from your original intent. Assume that param can be nil if you so desire:
Safe route, same conceptual behavior:
// GREAT
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
Safe route, slightly different behavior:
(See this response)
Use any thread in lieu of [NSThread mainThread].
// GOOD
[_controller performSelector:selector withObject:anArgument afterDelay:0];
[_controller performSelector:selector withObject:anArgument afterDelay:0 inModes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorInBackground:selector withObject:anArgument];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
Dangerous Routes
Requires some kind of compiler silencing, which is bound to break. Note that at present time, it did break in Swift.
// AT YOUR OWN RISK
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];
Because you are using ARC you must be using iOS 4.0 or later. This means you could use blocks. If instead of remembering the selector to perform you instead took a block, ARC would be able to better track what is actually going on and you wouldn't have to run the risk of accidentally introducing a memory leak.
Instead of using the block approach, which gave me some problems:
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
I will use NSInvocation, like this:
-(void) sendSelectorToDelegate:(SEL) selector withSender:(UIButton *)button
if ([delegate respondsToSelector:selector])
{
NSMethodSignature * methodSignature = [[delegate class]
instanceMethodSignatureForSelector:selector];
NSInvocation * delegateInvocation = [NSInvocation
invocationWithMethodSignature:methodSignature];
[delegateInvocation setSelector:selector];
[delegateInvocation setTarget:delegate];
// remember the first two parameter are cmd and self
[delegateInvocation setArgument:&button atIndex:2];
[delegateInvocation invoke];
}
If you don't need to pass any arguments an easy workaround is to use valueForKeyPath. This is even possible on a Class object.
NSString *colorName = #"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
UIColor *brightPink = [uicolor valueForKeyPath:colorName];
...
}
You could also use a protocol here. So, create a protocol like so:
#protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
#end
In your class that needs to call your selector, you then have a #property.
#interface MyObject
#property (strong) id<MyProtocol> source;
#end
When you need to call #selector(doSomethingWithObject:) in an instance of MyObject, do this:
[self.source doSomethingWithObject:object];

OCMock fails when expecting and calling isEqual on Custom isEqual implementation

Consider the following code
OCMockObject *mock = [OCMockObject mockForClass:[NSObject class]];
[[[mock expect] andReturnValue:OCMOCK_VALUE((BOOL){YES})] isEqual:[OCMArg any]];
[mock isEqual:[NSObject new]];
[mock verify];
Can someone, please, tell me why this fails with
test failure: -[NSObject_tests testIsEqualIsCalled] failed:
OCMockObject[NSObject]: expected method was not invoked: (null)
This makes literally no sense. I tried using andReturn: instead of andReturnValue: and still nothing.
expect returns an instance of OCMockRecorder. OCMock works by intercepting methods that OCMockRecorder does not implement. So you can't really mock methods defined by NSProxy, NSObject, or OCMockRecorder. You'll get the same results with this test:
-(void)testSomething {
id mock = [OCMockObject mockForClass:[NSObject class]];
[[[mock expect] andReturn:OCMOCK_VALUE((int){2})] hash];
[mock hash];
[mock verify];
}
If this is more than an academic exercise and you want to actually test a custom isEqual: implementation, there should be no reason to mock it. Just call isEqual: directly with objects that should and shouldn't match in your test.

How to start with OCMock and check if method was invoked

I'm trying to deal with OCMock. I created simple class MyClass.
#interface MyClass : NSObject
- (NSString *)simpleMethod;
#end
#implementation MyClass
- (NSString *)simpleMethod {
[self method];
return #"simple";
}
- (void)method {
NSLog(#"ABC");
}
#end
What I want to check is if method method was invoked when simpleMethod has been called. Now I've got following code but it doesn't work:
- (void)testMethodInvoked
{
id mock = [OCMockObject mockForClass:[MyClass class]];
[[mock stub] simpleMethod];
SEL selector = NSSelectorFromString(#"method");
[[mock expect] methodForSelector:selector];
[mock verify];
}
How should I test this case? I think that is pretty easy to do, but I have no idea how solve this problem.
How to create mock and call method simpleMethod which invoke method method?
Current log:
<unknown>:0: error: -[OCMockTestTests testOne] : OCMockObject[MyClass]: expected method was not invoked: methodForSelector:#selector(method)
You never actually create an object of the class that you want to test. Also, you have to expect first, then invoke the method:
- (void)testMethodInvoked
{
// first create an object that you want to test:
MyClass *object = [[MyClass alloc] init];
// create a partial mock for that object
id mock = [OCMockObject partialMockForObject:object];
// tell the mock object what you expect
[[mock expect] method];
// call the actual method on the mock object
[mock simpleMethod];
// and finally verify
[mock verify];
}
I sometimes find it useful to test "private" methods / implementations -- perhaps don't call it a unit test if that breaks some kind of orthodoxy -- but for a complex implementation I may want to verify behavior on a more granular level than through the external interface.
In any event, I will expose class extension methods by creating a category in the test class:
#interface MyClass (ExposeForTest)
- (void)method;
#end
- (void)testMyClass
{
id mock = [OCMockObject mockForClass:MyClass.class];
[[mock expect] method];
[mock simpleMethod];
}

Handling PVR textures for OpenGL ES 2 (the real questioning, though, is about Objective-C ARC and class methods)

According to the OpenGL ES Programming guide's section on texturetool,
Your app must parse the data header to obtain the actual texture data. See the
PVRTextureLoader sample for an example of working with texture data in the PVR format.
This is all well and good, but 3 years later, and PVRTextureLoader's PVRTexture.m doesn't compile because it needs to be converted in order to work in an ARC project. I reckon I could flag these two files as non-ARC but I wanted to at least learn a little about Objective-C this time around.
Here's a bit of code that's giving me trouble at this point:
+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
return [self initWithContentsOfFile:path];
}
This was manually converted from:
+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
return [[[self alloc] initWithContentsOfFile:path] autorelease];
}
Maybe someone could be so kind as to walk through what this actually does (as it makes no sense to me to refer to self from what is clearly declared as a class method and not an instance method), but the actual error seen is
<...>/PVRTexture.m:256:15: error: no known class method for selector 'initWithContentsOfFile:'
return [self initWithContentsOfFile:path];
^~~~~~~~~~~~~~~~~~~~~~
1 error generated.
As for why I am manually converting this file to ARC, the Edit->Refactor->Convert to Objective-C ARC... menu option basically says "fix these 8 errors before I can continue" and these 8 errors are of course ARC-related errors. Which I was hoping the conversion would be able to resolve. Circular dependencies are only fun the first time around.
The curious bit is that -initWithContentsOfFile:path is right there in the file too:
- (id)initWithContentsOfFile:(NSString *)path
{
if (self = [super init])
{
NSData *data = [NSData dataWithContentsOfFile:path];
_imageData = [[NSMutableArray alloc] initWithCapacity:10];
_name = 0;
_width = _height = 0;
_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
_hasAlpha = FALSE;
if (!data || ![self unpackPVRData:data] || ![self createGLTexture])
{
self = nil;
}
}
return self;
}
Note: This code compiles if I change the + to a - on the pvrTextureWithContentsOfFile declaration. I am positive the original code had a + there, so please, somebody help explain this to me.
alloc is a class method. You usually see alloc used from another class, with things like:
NSView *myView = [[NSView alloc] initWithFrame: someRect];
In a class method, self is the class, not an instance of that class. So [self alloc] allocates an instance of the class. It would also be valid to use the name of the class explicitly, so in the case of your PVRTexture class,
return [[self alloc] initWithContentsOfFile:path];
Could be replaced with
return [[PVRTexture alloc] initWithContentsOfFile:path];
Both are perfectly valid. I would probably use the second form, just because, like you, I find the first form a little odd-looking.
I got it, I think. Here's another example where writing out the question leads to the answer.
It's this:
return [[self alloc] initWithContentsOfFile:path];
I'm sure Google will be happy enough to (let SO) show me what alloc actually does. I guess calling [self alloc] from a class method is essentially what makes that method into a factory method that can generate instances of that class. This is nothing other than the Obj-C way to new something.
Me, I made the mistake of assuming alloc was something that ARC abolished.
Also, a side note: Reading carefully helps. I also just found out about GLKTextureLoader so I didn't need to convert PVRTextureLoader at all.

OCMock "expected method was not invoked" even though I'm calling it myself

I'm trying to set up a simple OCMock unit test in an iOS project, just to familiarize myself with the framework.
I have a mocked DataLoader class, and even though I'm calling the method myself, my expectation fails:
- (void)testSimpleMocking {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Override the 'dispatchLoadToAppDelegate:' to be a no-op
[[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Expect the method to be called
[[mock expect] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
However, when I run this test, I receive the error:
/Users/Craig/projects/MyApp/Unknown.m: -[MockingDataLoaderTest testSimpleMocking] : OCMockObject[DataLoader]:
expected method was not invoked: dispatchLoadToAppDelegate:<OCMAnyConstraint: 0x1a3d890>
How is this possible, when I am calling the method myself?
Edit: A more complex case:
- (void)testDataLoaderWaitsForDownload {
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
id metadataItem = [OCMockObject niceMockForClass:[NSMetadataItem class]];
// Prepare NSMetadataItem
[[[metadataItem expect] andReturn:nil] valueForAttribute:NSMetadataItemURLKey];
// CODERUN
[mock waitForDownload:metadataItem thenLoad:YES];
//VERIFY
[metadataItem verify];
}
And the implementation of the waitForDownload:thenLoad: method:
- (void)waitForDownload:(NSMetadataItem *)file thenLoad:(BOOL)load {
NSURL *metadataItemURL = [file valueForAttribute:NSMetadataItemURLKey];
...
Fails with the error:
Unknown.m:0: error: -[MockingDataLoaderTest testDataLoaderWaitsForDownload] : OCMockObject[NSMetadataItem]: expected method was not invoked: valueForAttribute:#"kMDItemURL"
In your test, stub is taking priority because it was called first. If you switch the order of your expect and stub your test should pass.
The reason you would use both expect and stub together (with the same argument expectations) is to ensure that at least one call occurs, but then to respond to subsequent calls without failure.
If you truly looking for just one call to a method, just add the andReturn: to the expect clause...
- (void)test_dispatchLoadToAppDelegate_isCalledExactlyOnce {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Expect the method to be called
[[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
An alternate scenario:
- (void)test_dispatchLoadToAppDelegate_isCalledAtLeastOnce {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Expect the method to be called
[[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Handle subsequent calls
[[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Call the method (again for fun!)
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
For this particular case, it looks like you could use niceMockForClass but if you wanted the stub to return a non-nil, then you'd have to call stub either way.
Ben Flynn is correct that reversing the order of stub and expect should make your test pass, but I'd go a step further and suggest you should remove the stub call. The way you've written this test suggests that stub is a prerequisite to expect, which it's not.
expect means the method must be invoked once and only once (for each expectation). stub means the method may be called zero or more times. Generally you expect calls that are important to the test, and stub things that are side effects. Or use a nice mock and only set expectations for the important calls.

Resources