How set a method as argument of class method in objective-c - iphone

I'm having a problem writing a class method wich have a method has argument.
The function is inside the class "SystemClass.m/h"
//JSON CALL
+(void)callLink:(NSString*)url toFunction:(SEL)method withVars:(NSMutableArray*)arguments {
if([self checkConnection])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *datas = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[arguments addObject:datas];
[self performSelectorOnMainThread:#selector(method:) withObject:arguments waitUntilDone:YES];
});
}else{
[self alertThis:#"There is no connection" with:nil];
}
}
What the function does is to call a JSON url, and give data to a Method
I use it like this:
[SystemClass callLink:#"http://www.mywebsite.com/call.php" toFunction:#selector(fetchedInfo:) withVars:nil];
but it crashes like this:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '+[SystemClass method:]:
unrecognized selector sent to class 0x92d50'
May you help me please? I'm trying to found the solution anyway!
Thanks, Alex

In you callLink method you already give a selector as argument (it's the argument called "method"). Moreover you need to add one more argument, because the "method" argument should be invoked from an object that implement this method (and in the example you give us, the applicaiton will try to call the method named "method" from SystemClass when you call :
[self performSelectorOnMainThread:method withObject:arguments waitUntilDone:YES];
Here self is the SystemClass and a such method doesn't seem to exist in SystemClass, that's why it is crashing). So add a target (an id object) to the arguments :
+(void)callLink:(NSString*)url forTarget:(id) target toFunction:(SEL)method withVars:(NSMutableArray*)arguments;
So for the following line you should just give the selector and call this selector on the target object :
[target performSelectorOnMainThread:method withObject:arguments waitUntilDone:YES];
And not :
[self performSelectorOnMainThread:#selector(method:) withObject:arguments waitUntilDone:YES];
Improvement :
Before calling the selector you should check if the target responds to the selector doing something like that (it'll prevent your application from crashing). Instead of doing this :
[target performSelectorOnMainThread:method withObject:arguments waitUntilDone:YES];
Do this :
if([target respondsToSelector:method])
{
[target performSelectorOnMainThread:method withObject:arguments waitUntilDone:YES];
}
else
{
//The target do not respond to method so you can inform the user, or call a NSLog()...
}

Related

EXC_BAD_ACCESS when accessing parameters in andDo of OCMock

I am trying to write an block of code using OCMock's stub andDo method.
In this case UIImageView extension class is being tested. I want to check that the extension calls [self setImage:] with parameter that is non-nil (later other image comparison will be used).
When using OCMock's andDo method, the test crashes with EXC_BAD_ACCESS after the block completes.
id mockView = [OCMockObject mockForClass:[UIImageView class]];
[[[mockView stub] andDo:^(NSInvocation *invocation)
{
UIImage *img;
[invocation getArgument:&img atIndex:2]; <---- line causing the exception
somebodySetImage |= (img != nil);
}] setImage:OCMOCK_ANY];
[mockView do_something_that_calls_setImage];
The only solution that I've found for now is using andCall instead of andDo, but this complicates the test.
Can I avoid the crash with andDo?
UPDATE
Well, I will try to give a better example here:
Here is the new piece of the test code:
- (void)testDownloadingThumbnail
{
PInfo *_sut = [[PInfo alloc] init];
__block id target = nil;
id mock = [OCMockObject mockForClass:[NSOperationQueue class]];
[[[mock expect] andDo:^(NSInvocation *inv)
{
NSInvocationOperation *op;
[inv getArgument:&op atIndex:2];
target = [[op invocation] target]; /* replacing this line with STAssert does not help either */
}] addOperation:OCMOCK_ANY];
[_sut setDownloadQueue:mock];
[_sut startDownloadingImagesAsync:YES];
[mock verify];
STAssertEqualObjects(target, _sut, #"invalid op target");
}
Here is the tested code (single method from PInfo):
- (void)startDownloadingImagesAsync:(bool)isThumbnailImg
{
NSInvocationOperation *inv;
inv = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadThumbnailWorker:)
object:nil];
[[self downloadQueue] addOperation:inv];
}
The code still crashes upon exit from startDownloadingImagesAsync with EXC_BAD_ACCESS.
If I add a breakpoint inside the andDo block, I see that the control reaches this point and retrieves correct objects via getArgument.
Yet, if I use getArgument inside the block, it crashes whatever I try to do.
P.S. Thanks for help.
I ran into a similar problem when using NSProxy's forwardInvocation: method.
Can you try the below?
NSInvocationOperation *op; // Change this line
__unsafe_unretained NSInvocationOperation *op; // to this line
Or another approach could be to retain NSInvocation's arguments:
[invocation retainArguments];
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/occ/instm/NSInvocation/retainArguments
I'll try to add a more detailed explanation later.
I think the problem is that you're trying to invoke a mock object directly. For what you're trying to do, you shouldn't need a mock object. Just call the method and verify that the image was set:
expect(myObject.imageView.image).to.beNil();
[myObject do_something_that_calls_setImage];
expect(myObject.imageView.image).not.to.beNil();
If you really want to use a mock for some reason, you could do it with a real UIImageView and a partial mock:
UIImageView *imageView = myObject.imageView;
id mockView = [OCMockObject partialMockForObject:imageView];
__block BOOL imageSet = NO;
[[[mockView stub] andDo:^(NSInvocation *invocation) {
UIImage *img;
[invocation getArgument:&img atIndex:2];
imageSet = (img != nil);
}] setImage:OCMOCK_ANY];
[myObject do_something_that_calls_setImage];
expect(imageSet).to.beTruthy();
In my case this was happening because I introduced another parameter to this method, so the block parameter got shifted by one.
I fixed it by changing [inv getArgument:&op atIndex:2] to [inv getArgument:&op atIndex:3]

Possible to pass [self anyFunction] in blocks without __weak object (iOS 5 + ARC)

Is it possible to pass [self anyFunction] in blocks without a __weak object from self?
As an example this is valid code from the System Framework:
[UIView animateWithDuration:0.8 animations:^{
//Do animationStuff
} completion:^(BOOL finished) {
[self anyFunction];
}];
You can pass [self anyFunction] in the completion block without a warning. But if you write your own method with a completion block, the following warning occurs: capturing 'self' strongly in this block is likely to lead to a retain cycle.
A working solution is quite simple (iOS 5 + ARC). Before the block declare:
__weak MyClass *weakSelf = self;
and in the completion block you have to call:
[weakSelf anyFunction];
But, back to my Question: Why there is no need in the System Framework APIs to use a __weak object and to use self without any warnings. And how to implement a method without the need of a __weak object in the block?
Thank you for your effort.
The blocks which throw up the error are ones where you capture the objects that own the block. For example
[object performBlock:^{
[object performSomeAction]; // Will raise a warning
}];
or
[self performBlock:^{
[self doSomething]; // Will raise a warning
}];
but
[self performBlock:^{
[object doSomething]; // <-- No problem here
}];
Because an object retains its blocks, and a block retains it's objects. So in both these cases, the object which performs the block owns the block, which also owns the object. So you have a loop - a retain cycle. which means the memory is leaked.
In the example you have given - you're looking at a class method. You're calling the block on a UIView class, not a UIView object. A class has no memory associated with it. And you are probably calling this function from a controller, so the self reference is being retained by the block, but there is no loop because self is not retaining the block.
In the same way that, you may have noticed, not all objects that are used in the block need to be weakly referenced - just the ones that cause a retain cycle.
On code that I need to compile potentially with or without ARC, or with or without the newer compilers, I do the following ... functionally it's the same as what you've listed already, but it avoids the__weak and also avoids the retain release cycles:
//
// FOR NON-ARC PROJECTS
//
__block __typeof__(self) bself = self;
[someObject doThingWithBlock:^(id result){
if (!bself)
return;
bself.thingWhich = result;
}];
///
// FOR ARC PROJECTS
//
__weak MyClass *bself = self;
[someObject doThingWithBlock:^(id result){
if (!bself)
return;
bself.thingWhich = result;
}];

How to disable function calling of selector in cocos2d?

I have set a function in a selector. After a function call I want to not call selector. I wrote this line of code but it is throwing an exception:
self.touchselector = nil;
How can I reset the function of selector in cocos2d?
Try this one:
[NSNotification cancelPreviousPerformRequestsWithTarget:self selector:#selector(powerHide) object:nil];
Assuming you scheduled a selector similarly to this:
[self scheduleSelector:#selector(onTouch:) interval:1];
Then you can unschedule that particular selector in the method that it calls via _cmd:
-(void) onTouch:(ccTime)delta
{
// this will stop onTouch from being called every second
[self unscheduleSelector:_cmd];
}

#selector in objective-c not behaving as expected

I am a little perplexed and I have been working on this for hours and googling without any real leads. I want to create a callback in objective-c for my iPhone app utilizing the #selector.
Class 1:
- (void) someMethod {
// create selector
SEL successCallback = #selector(successMethod);
// call some service with caller and selector
[class2 dispatchSomeEvent:self callback:successCallback];
// here's the call back method
- (void) successMethod {
NSLog(#"Callback success");
}
}
Class 2:
// some event
- (void) dispatchSomeEvent:(id) caller selector:(SEL) successCallback {
// catch the event and execute callback
if ([caller respondsToSelector:successCallback]) {
[caller successCallback];
}
}
The conditional respondsToSelector will pass but the callback on the next line will fail. HOWEVER, if I would do like this:
// catch the event and execute callback
if ([caller respondsToSelector:successCallback]) {
[caller successMethod];
}
So instead of using the selector I passed, I type in the method name directly... and it works!
The error I get is this:
unrecognized selector sent to instance
0x6c37f70
What is going on here??
Thanks in advance!
You should call your selector using -performSelector method:
if ([caller respondsToSelector:successCallback]) {
[caller performSelector:successCallback];
}

delegate method throws runtime "unrecognized selector" error when switching back from one UIViewController to the main View Controller

Ok, I've spend like half day on this and it's killing me.
So I've got 3 view controllers transitioning from one another, something like this:
I call the UploadDecisionViewController after destroying the previous View Controller:
[self dismissModalViewControllerAnimated:YES];
[self performSelector:#selector(showUDModalView) withObject:nil afterDelay:0.5];
In my showUDModalView method:
- (void)showUDModalView
{
UploadDecisionViewController *udcontroller = [[UploadDecisionViewController alloc] initWithNibName:#"UploadDecisionViewController" bundle:nil];
udcontroller.delegate = self;
[self presentModalViewController:udcontroller animated:YES];
[udcontroller release];
}
The UploadDecisionViewController shows up no problem. The UploadDecisionViewController has a button, which when clicked I want it to transition to the FileUploadViewController. I setup a UploadDecisionDelegate, threw a method in there to handle the button clicking:
Inside UploadDecisionDelegate protocol (UploadDecisionViewController.h):
#protocol UploadDecisionDelegate
//let UOnliveViewController know that a button was selected
- (void)UploadDecisionViewController:(UploadDecisionViewController *)controller madeChoice:(NSString *)whichDirection;
#end
Then inside my IBAction method when the button is clicked, I have this:
- (IBAction)decisionSelected:(id)sender
{
[delegate UploadDecisionViewController:self madeChoice:#"upload"];//crashing at this line
}
When I run this, at this line above it is throwing a runtime exception:
2010-06-09 12:48:59.561 UOnlive[4735:207] *** -[UIView UploadDecisionViewController:madeChoice:]: unrecognized selector sent to instance 0x3b65420
2010-06-09 12:48:59.562 UOnlive[4735:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIView UploadDecisionViewController:madeChoice:]: unrecognized selector sent to instance 0x3b65420'
2010-06-09 12:48:59.563 UOnlive[4735:207] Stack: (
33502299,
2495698185,
33884219,
33453686,
33306306,
20618,
2982917,
3390286,
3399023,
3394235,
3087839,
2996168,
3022945,
40156505,
33287040,
33283144,
40150549,
40150746,
3026863,
11700,
11554
)
Let me throw in the delegate method implemented also:
- (void)UploadDecisionViewController:(UploadDecisionViewController *)controller madeChoice:(NSString *)whichDirection
{
NSLog(#"it got to here 245");
[self dismissModalViewControllerAnimated:YES];
if (yesOrNo) {
//open up the FileUploadViewController and proceed to upload
[self performSelector:#selector(showFUModalView) withObject:nil afterDelay:0.5];
}
}
Can someone tell me what the heck is going on? Thanks a bunch for the help...
The error says that you are trying to call the UploadDecisionViewController method on UIView.
My bet is that you set some view to the delegate instead of view controller.
Where the showUDModalView method is located?
Maybe you set the delegate in some additional places?
Your code is to lengthy and I dont want to go through this but just an advice, you can c if your object can/cannot perform a selector with this kind of statement:
if([myObj respondsToSelector:#selector(myFunc)])
{
//do something
}
else{
//do something else
}