performSelector throwing invalid argument, why? - ios

This line of code works perfectly
[self explodeBomb:obj];
but if I replace it with the following line, I get an NSInvalidArgument Exception, with the reason being an unrecognized selector.
[self performSelector:#selector(explodeBomb) withObject:obj ];
The definition of the method is as follows:
-(void)explodeBomb:(SKNode *)bomb
I know, this has to be me not understanding something fundamental. But why I am able to call the method directly with no problems, but when I try to use the performSelector it blows up? For the record obj is defined as an ID. I tried changing the signature of explodeBomb to take an ID and then explicitly cast it inside the method, but that threw the same exception. Anyone know what the heck I am doing wrong?

Use : and write like below
[self performSelector:#selector(explodeBomb:) withObject:obj ];
Since your method explodeBomb has an argument so you have to specify :

Related

Unexpected OCMMacroState instead of mocked object

This code used to work and stopped with Xcode 8. I'm not sure what exactly is wrong, is it some premature value inside the block?
I am getting:
failed: caught "NSInvalidArgumentException", "-[OCMMacroState productIdentifier]: unrecognized selector sent to instance 0x7fb07dc885a0"
While the code is:
OCMVerify([mockPaymentQueue addPayment:[OCMArg checkWithBlock:^BOOL(SKPayment *payment) {
return [payment.productIdentifier isEqualToString:#"testID"];
}]]);
Any idea how to fix it or what's wrong?
Just saw this now. What happens when you rewrite the code as follows?
id productIdentifier = payment.productIdentifier;
OCMVerify([mockPaymentQueue addPayment:[OCMArg checkWithBlock:^BOOL(SKPayment *payment) {
return [productIdentifier isEqualToString:#"testID"];
}]]);

isKindOfClass returning different values for Test Cases

I have a method someMethod. This method, at some point has the following if-else condition.
- (void) someMethod {
// ... some more code ...
if ([userArray[0] isKindOfClass:[Me class]]) {
// some code
}
else {
// some other code
}
}
Now this if-condition is always met when I execute the code normally. But when I call it from one of my test-cases, the else-part gets executed instead. I am calling this method exactly the same way (it has no side-effects, etc).
When I debugged the thing in both normal run, and testing run. I saw something different.
While running in Test, the userArray had 1 object, (Me_Me_2 *)0x00007fa61d39dbf0.
And while running it normally, the userArray had the same object, but there was one difference. It said (Me_Me_ *)0x00007fce71459ae0.
When I print the value of NSStringFromClass([userArray[0] class]), they both print "Me".
"Me" is a NSManagedObject.
Another interesting thing is, if I add an expression in the debugger and evaluate it, it always evaluates to true - ([((NSObject*)userArray[0]) isKindOfClass:[Me class]]) returns (bool)true. This is totally bizarre! If the condition is true, why does it ever go into the else block?
Now some questions -
What is happening over here? Are Core Data objects treated different when running in tests?
Why is the type of the object "Me_Me_2" while testing and "Me_Me_" otherwise? Why is it not just "Me"?
This sounds similar to the following issue: isKindOfClass doesn't work as expected
In short, is the class being compared a target member of the test target? It should only be a target member of the application.

Odd error (UITextSelectionView) while calling a method?

I'm facing a problem while calling a method and I don't know how figure it out.
Basically, during the main menu, I want to call a SKNode showing a tutorial part. The code is the following:
- (void)didMoveToView:(SKView *)view
{
...
if ([[GameData sharedData] openingTutorial]) { // Checks if the menu needs the tutorial
[_tutorialObj performSelector:#selector(runTutorialWithStep:)
withObject:[NSNumber numberWithInt:8]
afterDelay:3.0
];
}
}
When the method didMoveToView: is called (even before waiting the 3 seconds for the runTutorialWithStep:), I got this error:
[UITextSelectionView name]: unrecognized selector sent to instance 0x1576e6c0
2014-10-14 11:01:19.430 [406:130379] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITextSelectionView name]: unrecognized selector sent to instance 0x1576e6c0'
The odd thing is that in the previous class I use the same tutorial's action in the didMoveToView: and it's working right. But...
Thing is getting more strange here!!!
If I use an intermediary method for this calls:
- (void)didMoveToView:(SKView *)view
{
...
[self performSelector:#selector(intermediaryMethod)
withObject:nil
afterDelay:3.0
];
}
- (void)intermediaryMethod
{
[_tutorialObj performSelector:#selector(runTutorialWithStep:)
withObject:[NSNumber numberWithInt:8]
afterDelay:0.0
];
}
Everything works without issues. I don't want to avoid the problem but solve this. Any advices?
The error says it all. You try to send a 'name' message to object that doesn't implement it (UITextSelectionView). Since your second take works the cause is either in
[[GameData sharedData] openingTutorial]
method or before this call. Search for objects calling 'name' and check if it's the UITextSelectionView in question.
That or maybe you have weak reference to general view object and during those three seconds before calling runTutorialWithStep you reassign it to object that implements 'name' message.
Ok, I solved the problem.
I added a breakpoint for every exception and I realized that the issues was due to an other class (the only one with UITextView).
Basically I removed the text field from its parent ([self removeFromParent]) by my own once I did not need it anymore.
I suppose the error was fired during the deallocation 'cause the program can't find the UITextView. I managed to remove this statement and everything works right.
Actually I still have doubts because I don't understand why this exception is throw only if I call the [_tutorialObj runTutorialWithStep:] method.
I found the way to fix it.
UITextView *textView = .....
textView.selectable = NO;

OCMock: mocked protocol isn't stopped correctly

I have a test case using OCMock which does the following:
CAAOAuth2AuthenticationManager *oAuth2AuthManager = [[CAAOAuth2AuthenticationManager alloc] init];
id authDelegate = [OCMockObject mockForProtocol:#protocol(CAAAuthenticationDelegate)];
id partialAuthManagerMock = [OCMockObject partialMockForObject:oAuth2AuthManager];
id resultMock = [OCMockObject mockForClass:[CAAOAuth2AuthenticationResult class]];
[[authDelegate reject] didFailWithError:OCMOCK_ANY];
[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock reject] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];
[[[resultMock expect] andReturnValue:OCMOCK_VALUE(YES) ] isAuthenticated];
[[resultMock reject] refreshToken];
When I run the test cases, a second test case (completely different test class and file) which also uses the CAAAuthenticationDelegate protocol fails with SIGABRT:
2014-02-28 10:11:24.594 otest[37161:303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[CAAAuthenticationDelegate]: unexpected method invoked: didReceiveAuthenticationWithResult:OCMockObject[CAAOAuth2AuthenticationResult]
stubbed: didFailWithError:<OCMAnyConstraint: 0xa913fc0>'
But, I don't use any mocks in the second test case. I was trying to clear the mocks with stopMocking with no success.
The following mock setup works without any problems:
[[authDelegate reject] didFailWithError:OCMOCK_ANY];
[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock expect] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];
[[[resultMock expect] andReturnValue:OCMOCK_VALUE(NO) ] isAuthenticated];
[[[resultMock expect] andReturn:refreshToken] refreshToken];
Can someone tell me, why this happens?
As a workaround, can you create an empty implementation of the protocol and then mock a real object? I've had better luck with that method -- mocking protocols has only led to wonkiness for me.
#interface TestAuthDelegateImpl : NSObject <CAAAuthenticationDelegate>
#end
#implementation
- (void)didFailWithError:(id)whatever;
#end
Something like that. Then just mockForClass it -- might be better behaved.
That seems to mean that your CAAOAuth2AuthenticationManager instance was still around in a later test, and still had the old mock delegate set on it, and some method was called on it which caused that delegate method to be called. Is CAAOAuth2AuthenticationManager a singleton-type object, or is the same instance used in the second test? I would reset the delegate to nil on the auth manager in the first test when it is done.
You can also use niceMockForProtocol, which will silently ignore any method calls which do not have an explicit reject set up on them. By the exception, it sounds like the reject has been removed, and the delegate mock will now just throw exceptions on any method sent to it, since there are also no expects set up.
Also, I would use STAssertNoThrow() around the actual call to your real code (which presumably happens after the setup you show above). Rejects and unexpected methods will raise exceptions, which can cause the mock objects to not get deallocated properly and create issues for subsequent tests. If the test in question passed though, that is probably not an issue here.
The last thing to check is if your delegate property is declared as "assign" instead of "weak". If it's "assign", and you don't nil it out and it gets freed, then anything could happen (segfault, or an entirely new object getting allocated at that same memory address). That also seems unlikely here though.

How can I find the sender of an unrecognized selector in LLDB?

I'm getting an "unrecognized selector" error that is confusing me.
I know that I can "po" the pointer in LLDB to find out about the receiver. But is there a way to deduce which object is sending this message?
Thanks!
The command bt in the debugger will show you a backtrace (stacktrace), which should give you the class that initiated the message somewhere in that output.
Backtrace doesn't always help if you're dealing with multiple threads - you end up with the backtrace of the exception handler on the main thread which isn't necessarily the one that cause the error.
However, since you know that the particular selector doesn't exist for a particular class, you can cheat a little by using a category to add the selector to the class, then just stick a breakpoint on it.
For example, for this error:
-[__NSCFDictionary isEqualToString:]: unrecognized selector sent to instance 0x10004fb0
we know that something's trying to call "NSDictionary" with "isEqualToString". So, add this at the end of any file you like, outside any other "#implementation" blocks:
#implementation NSDictionary(debug)
- (BOOL)isEqualToString:(NSString*)theString {
return FALSE;
}
#end
Stick a breakpoint on this, re-run your code and you will get a genuine stack trace. Don't forget to remove the category once you've found the error!

Resources