Timed selector never performed - iphone

I've added an observer for my method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(closeViewAfterUpdating)
name:#"labelUpdatedShouldReturn"
object:nil];
Then my relevant methods:
-(void)closeViewAfterUpdating; {
NSLog(#"Part 1 called");
[self performSelector:#selector(closeViewAfterUpdating2) withObject:nil afterDelay:2.0];
}
-(void)closeViewAfterUpdating2; {
NSLog(#"Part 2 called");
[self dismissModalViewControllerAnimated:YES];
}
The only reason why I've split this method into two parts is so that I can have a delay before the method is fired.
The problem is, the second method is never called. My NSLog output shows Part 1 called, but it never fires part 2. Any ideas?
EDIT: I'm calling the notification from a background thread, does that make a difference by any chance?
Here's how I'm creating my background thread:
[NSThread detachNewThreadSelector:#selector(getWeather) toTarget:self withObject:nil];
and in getWeather I have:
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateZipLabel" object:textfield.text];
Also, calling:
[self performSelector:#selector(closeViewAfterUpdating2) withObject:nil];
does work.
EDITx2: I fixed it. Just needed to post the notification in my main thread and it worked just fine.

The background thread is the problem. It has a non running run loop, thus the selector is never called. Just let the NSRunLoop or CFRunLoopRef object of the thread run while the selector isn't fired.

I tried your code and it works fine on my side. You might be doing something funky in the background that is interrupting your selector.

You have a semi-colon in the method definition:
-(void)closeViewAfterUpdating2; {
Is this present in the code or a copy/paste issue? That would be the problem on why you never see it called.

Related

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

need Database writing to happen in NSNotification call-back method

I make a call to a web service, passing a parameter and then register an observer in a viewcontroller class (to notify completion of download) :
[self callWebservice:parameter1];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataDownloadComplete:) name:OP_DataComplete object:nil];
and then post a notification in my parser class:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection method of the parser class. [[NSNotificationCenter defaultCenter] postNotificationName:OP_DataComplete object:nil];
In the callback method dataDownloadComplete: i would like to call the same web service again several times.
-(void)dataDownloadComplete
{
if([anArray objectAtindex:N]<10)
{
[self callWebservice:parameterN];
NSLog(#"This is getting called everytime (9 times)");
[self writeintoDatabase];
N++;
}
}
But the problem is that i want to write into a database what data i download from the service. The DB writing happens for 'parameter1' call strangely, and goes on for the others but not for parameter9(which i need as well). Note the Log is called all 9 times though. The writeintoDatabase code is perfect. Please help. Thanks in advance.

Simulate asynchronous function call

I have an asset manager that needs to notify the owner it's assets are ready. I'm sending a token back for the consumer to listen to listen for a notification to avoid tighter coupling. The issue is when the assets are already loaded I need to call the loadComplete after a delay. What's the best way to do this in objective-c?
Asset Manager
-(tokenString*) loadAssetPath:(NSString*) asset {
//start asynchronous load
//or if assets ready send complete <-- issue
return nonceToken;
}
-(void)loadComplete {
[[NSNotificationCenter defaultCenter]
postNotificationName:tokenString object:self];
}
Consumer
NSString* token;
-(void) loadSomething {
if(token)
[self removeListener];
token = [[AssetManager sharedManager]
loadAssetPath:#"http://server.dev/myLargeImage.png"];
[[NSNotificationCenter defaultCenter]
addObserver:[AssetManager sharedManager]
selector:#selector(assetLoaded:) name:token];
}
-(void)assetLoader:(NSNotifcation*)aNotification {
[self removeListener];
//continue on with stuffing stuff
}
Use NSObject's performSelector function which allows it to be called after a delay.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
You can even use a form of this function to run it on another thread, which is useful to not blocking the main thread when doing lengthy operations (just don't muck with the UI objects in this thread).
#DavidNeiss is correct about performSelector:withObject:afterDelay:, but you almost certainly don't want an actual time delay here. At most you want to perform your selector on the next event loop, just so things are consistent for the listener. So you should make the delay 0. This differs from the normal performSelect:withObject: which will immediately perform the selector synchronously.
-(tokenString*) loadAssetPath:(NSString*) asset {
//start asynchronous load
if (<load is actually complete>) {
// -loadComplete will execute on the next event loop
[self performSelector:#selector(loadComplete) withObject:nil afterDelay:0];
}
return nonceToken;
}

UIAlertView clickedButtonAtIndex EXC_BAD_ACCESS

I currently have a UIAlertView being shown with two option buttons. When the user presses one of the buttons, I would like a method (in the same object) to be called that would then retrieve a file from the web.
I can call the method fine and can call NSLog(), but as soon as I come to use any object variables, I get an EXC_BAD_ACCESS error.
My first thought was it could be a threading issue, so thought calling NSNotificationCenter might solve it, but that too ends in the same error.
Below is the code I've got at the moment. I have tried a few different things (some are commented out) to no avail. The 'dbURL' object is a property of the class. (Edit: below code is not complete)
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
//If database update
[[NSNotificationCenter defaultCenter] postNotificationName:#"newdb" object:self]; //EXC_BAD_ACCESS happens here
if ([alertView.title isEqualToString: #"Database Update"]){
switch (buttonIndex) {
case 0:
//[self getNewDatabase];
//[self performSelectorOnMainThread:#selector(getNewDatabase) withObject:nil waitUntilDone:NO];
//[NSThread detachNewThreadSelector:#selector(getNewDatabase) toTarget:self withObject:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"newdb" object:self];
break;
// Get a new database file from the server
- (void)getNewDatabase{
NSLog(#"in database: %#", dbURL);
}
Thanks in advance!
The problem was an object I was calling as not being retained properly and therefore the reference was lost, resulting in an EXC_BAD_ACCESS error
Thanks to Nick Weaver. Using the NSZombieEnabled argument in the build options helped to identify the rogue reference.
You can't compare NSString with == . You need to use isEqualToString:
[alertView.title isEqualToString:#"Database Update"]

Removing observers for an NSOperation

I have a view which loads data via an NSOperation within an NSOperationQueue. I want to allow users to leave this view before the operation has completed. My problem is that I can't seem to consistently do this without crashing. Here is my code to start the operation:
NSOperationQueue* tmpQueue = [[NSOperationQueue alloc] init];
self.queue = tmpQueue;
[tmpQueue release];
SportsLoadOperation* loadOperation = [[SportsLoadOperation alloc] init];
[loadOperation addObserver:self forKeyPath:#"isFinished" options:0 context:NULL];
[self.queue addOperation:loadOperation];
[loadOperation release];
If I leave the view while the operation is still executing, I often get this error:
[SportsViewController retain]: message sent to deallocated instance 0x38b5a0
If I try to remove the observers so that this doesn't occur, like this:
-(void)viewWillDisappear:(BOOL)animated {
if (self.isLoadingData) {
for (NSOperation *operation in [self.queue operations]) {
if([operation isExecuting]) {
[operation cancel];
[operation removeObserver:self forKeyPath:#"isFinished"];
}
}
}
[super viewWillDisappear:animated];
}
Then I sometimes get this error:
Terminating app due to uncaught exception 'NSRangeException', reason:
'Cannot remove an observer <SportsViewController 0x661c730> for the key path "isFinished" from <SportsLoadOperation 0x66201a0> because it is not registered as an observer.'
How can I avoid these problems?
The 2nd error message says it all.
Have you tried to not removeObserver after [operation cancel] and see what happens then?
Have you tried to first removeObserver and only then cancel the operation?
These might help to narrow down the conditions that trigger the error. Also, you might want to add log output to the code to see when it actually executes.
And, like freespace's answer says, adding & removing observers is best done in the construction / destruction methods of the observed instances. This generally yields more stable code.
Looks like you have an instance of SportsLoadOperation that doesn't have SportsViewController as an observer. Are you inserting SportsLoadOperation anywhere else in your code? If this is the case, consider writing an initWithObserver method for SportsLoadOperaion that will do the observing automatically. This avoids errors caused by forgetting to set the observer on isFinished.
Also, it is probably better to do the removal of observer in dealloc then in viewWillDisappear because viewWillDisappear is called in many circumstances, e.g. when displaying a modal view controller. Thus you might be prematurely stopping your operations.
Edit
Instead of checking against [operation isExecuting] check against [operation isCancelled]. This is because [operation cancel] doesn't stop an operation - it can and will continue executing if you don't actually check for isCancelled in your main method. This means that if viewWillDisappear is called twice, you could end up attempting to call removeObserver twice on the same instance of SportsLoadOperation, with the second attempt failing.
Add some debugging output statements in the following places:
when you create a SportsLoadOperation instance and insert it into the queueu
when you are cancelling a SportsLoadOperation instance and removing from it observers.
I ended up solving this by overriding the observed operation's addObserver and removeObserver methods, to keep track of observers so I could remove them in the [cancel] method.
All I have to do now is call the operation queue to cancel all operations before I dismiss the controller that was observing the operations.
Below, _observers is an NSMutableDictionary.
- (void)addObserver:(NSObject*)observer
forKeyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options context:(void*)context
{
[super addObserver:observer forKeyPath:keyPath options:options context:context];
[_observers setValue:observer forKey:keyPath];
}
- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath
{
[super removeObserver:observer forKeyPath:keyPath];
[_observers removeObjectForKey:keyPath];
}
- (void)cancel
{
[super cancel];
for(id key in _observers)
{
id object = [_observers valueForKey:key];
[super removeObserver:object forKeyPath:key];
}
[_observers removeAllObjects];
}