#selector in objective-c not behaving as expected - iphone

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];
}

Related

Swift JSValue callWithArguments() within nested completion handler results in BAD_ACCESS

I have a simple function that takes a completion handler as a JSValue. It's a JSValue because I'm using this function as part of JSExport protocol.
This function then calls another internal method with another completion handler. When this second handler is called, I want to callWithArguments on the JSValue.
This all works as expected when I callWithArguments from outside the second completion handler, but I get a BAD_ACCESS when calling from the second handler.
func myFunction(completion: JSValue) {
// If I put completion.callWithAttributes([]) here, everything works fine.
self.mySecondFunction(completion: {(result: Dictionary<String, AnyObject>) -> Void in
// If I put completion.callWithAttributes([]) here, I get a BAD_ACCESS
})
}
Any help greatly appreciated. Thanks!
I strongly suggest you to do the following
[self.callback.context[#"setTimeout"]
callWithArguments:#[callback, #0, items]];
when you are going to send response to the JavaScriptCore counterpart. This will prevent the TVML UI MainThread to hang. As you can see it's a call of the setTimeout javascript function with delay 0, your callback and items as parameters like:
setTimeout(callback,0,items)
I'm not sure how you are creating the alert anyways here is one from Apple:
createAlert : function(title, description) {
var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<alertTemplate>
<title>${title}</title>
<description>${description}</description>
<button class="btn_close">
<text>OK</text>
</button>
</alertTemplate>
</document>`
var parser = new DOMParser();
var alertDoc = parser.parseFromString(alertString, "application/xml");
return alertDoc
}
There is no direct relationship with the alert and the behavior you are seeing here, it's more a side effect of calling this
completion.callWithArguments([])
in a unexpected way. It's better that you save your completion somewhere, and get a reference to it on the object instance. Then, when the long task ends, you call it. Also if you are performing a long task, it's reasonable that you move everything in a NSOperation like this:
/** JavaScriptCore Callback Operation */
#interface JSCallbackOperation: NSOperation
#property(nonatomic, strong) JSValue*callback;
#property(nonatomic, strong) id items;
#end
#implementation JSCallbackOperation
- (id)initWithItems:(id)items callback:(JSValue*)callback {
if(self = [super init]) {
self.items=items;
self.callback=callback;
}
return self;
}
- (void)main {
#autoreleasepool {
if(self.callback) {
NSLog(#"Dispatching %#", self.callback);
[self.callback.context[#"setTimeout"]
callWithArguments:#[self.callback, #0, self.items]];
}
}
}
At this point you define a helper a call the callbacks with parameters then:
#pragma mark - API Helper
- (void)handleResponseWithItems:(id)items callback:(JSValue*)callback {
NSArray *active_and_pending_operations = operationQueue.operations;
NSInteger count_of_operations = operationQueue.operationCount;
NSLog(#"Running operations: %ld of %ld", active_and_pending_operations.count, count_of_operations);
JSCallbackOperation *op = [[JSCallbackOperation alloc] initWithItems:items callback:callback];
[op setQueuePriority:NSOperationQueuePriorityNormal];
[op setCompletionBlock:^{
NSLog(#"Operation completed.");
}];
[operationQueue addOperation:op];
}

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

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()...
}

Facebook loginViewFetchedUserInfo is called twice

I am using facebook SDK 3.0 in my app. The delegate method is called twice when after logging to facebook.
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
//loginThroughFb=TRUE;
NSString *userId=[[NSString alloc] initWithString:[user id]];
[self soapCallForLogin:#"" password:#"" deviceId:#"" fbid:userId];
NSLog(#"%#",userId);
[userId release];
}
I tried 'HelloFacebookSample' project and the method is called only once.
So I guess the best solution for such case is to keep a reference to the last user object and compare it to the new object you get the next call, and if they're equal you can just ignore that call.
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user {
if (![self isUser:cachedUser equalToUser:user]) {
cachedUser = user;
/// Do something
}
}
- (BOOL)isUser:(id<FBGraphUser>)firstUser equalToUser:(id<FBGraphUser>)secondUser {
return
[firstUser.objectID isEqual:secondUser.objectID] &&
[firstUser.name isEqual:secondUser.name] &&
[firstUser.first_name isEqual:secondUser.first_name] &&
[firstUser.middle_name isEqual:secondUser.middle_name] &&
[firstUser.last_name isEqual:secondUser.last_name] &&
...
}
I also had this problem. I managed to fix it with an ugly hack, but it works. I keep a counter in the FBLoginView delegate. When the fetchedUserInfo is called, I check the counter. If it is greater than zero, return. Otherwise, do two things -
1. increment the message counter
2. Fire a delayed event that zeroes the message counter again.
So your fetchedUserInfo method will look like this:
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
if ([self messageCounter] >0)
return;
else
{
self.messageCounter++;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
[self setMessageCounter:0];
});}
// Do whatever you were going to do }
Fixed in FB SDK 3.8 released on Sept 18 2013. The delegate methods are now called once per login regardless of how many times the repeated logging out and back in occur.
I was also able to reproduce this on FB SDK 3.7.1 and within their own sample program "Scrumptious"
As mentioned (at least for me) this only happens after:
Logging in once
Logging out
Logging back in (Now it happens)
What is interesting is the order of calls on re-logins:
On the first login I the calls I see are:
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView;
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user;
On the 2nd (and later) logins I see:
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user;
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView;
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user;
Which gives a handy little workaround of setting a flag in the middle method like so:
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
// Set flag
self.isFirstLoginDone = YES;
}
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user {
// Check
if(self.isFirstLoginDone) {
// Execute code I want to run just once
NSLog(#"fetched");
}
// Don't forget to clear the flag (I guess it shouldn't matter if everything is cleaned up)
self.isFirstLoginDone = NO;
}
There could be another reason, which i jsut faced.
My situation:
ViewController A has a login (With fbloginview and its delegate set)
User chooses to register, moves to ViewController B with another fbloginview and its delegate set.
The above makes the delegate fire twice.
I have fixed this by setting delegate to nil on ViewWillDisappear in ViewController A.
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
fbLoginButton.delegate=self;
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
fbLoginButton.delegate=nil;
}
I used this simple trick :
(Define an int facebookCounter in your interface)
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
if (self.facebookCounter==0) {
self.facebookCounter++;
return;
}
//Do stuff here
}
I needed to add thread safety in this method. A simple class variable did not work. The following two options will work, depending on the use case-
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user {
//self.executedOnce = NO; in the init method of this class
#synchronized(self){
if(!self.executedOnce) {
//do something once per init of this class
self.executedOnce = YES;
}
}
//OR- This will only execute once in the lifetime of the app, thus no need for the executedOnce flag
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//do something once per lifetime of the app
});
}
just in the loginViewFetchedUserInfo method set the delegate of the loginView to nil. then it can never be called. and if you need the login again, set the delegate to the correct object.

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];
}

Wait for MKReverseGeocoder to provide address

Is there a way to wait for geocoder to invoke didFailWithError or didFindPlaceMark?
My problem is that i have to call a method that receives coordinate and returns placemark holding the address. But when i call [myGeocoder start] code continues and i get an empty placemark.
My code is:
- (MKPlasemark*) getAddress:(CLLocationCoordinate2D) coordinate
{
[self startGeocoder:coordinate];
return self.foundPlasemark;
}
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFindPlacemark:(MKPlaseMark*)plasemark
{
self.foundPlasemark=plasemark;
}
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error
{
self.foundPlasemark=nil;
}
I tryed to perform sleep() whyle one of the following methods invoked, but it didn't work.
I think you are going about it the wrong way, there is no reason to block, what you have to do is have that method return void, and in the class that is handling the geocoding, define a protocol that has a method say -(void)didReceivePlacemark:(id)placemark, placemark can be nil or some placemark, and it is called when the geocoder returns. You also make a delegate property for your class so anyone can subscribe to the protocol... Then in the calling class subscribe to the protocol and implement the method...heres a bit more on protocols
Hope that helps
Here is an example:
So the interface of your class that does the geocoding will look something like this
#protocol GeocoderControllerDelegate
-(void)didFindGeoTag:(id)sender; // this is the call back method
#end
#interface GeocoderController : NSObject {
id delegate;
}
#property(assign) id <GeocoderControllerDelegate> delegate;
Then in the implementation you would see something like this
- (void) getAddress:(CLLocationCoordinate2D) coordinate
{
[self startGeocoder:coordinate];
}
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFindPlacemark:(MKPlaseMark*)plasemark
{
[delegate didFindGeoTag:plasemark];
}
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error
{
[delegate didFindGeoTag:nil]
}
In the calling class, all you have to set is the delegate property of the GeocoderClass, and implement the protocol, the implementation might look somethign like
-(void)findMethod
{
GeocoderController *c=...
[c setDelegate:self];
[c findAddress];
//at this point u stop doing anything and just wait for the call back to occur
//this is much preferable than blocking
}
-(void)didFindGeoTag:(id)sender
{
if(sender)
{
//do something with placemark
}
else
{
//geocoding failed
}
}