Delegate functions not being called - iphone

Long time lurker, first time poster.
I'm making a ServerConnection module to make it a whole lot modular and easier but am having trouble getting the delegate called. I've seen a few more questions like this but none of the answers fixed my problem.
ServerConnection is set up as a protocol. So a ServerConnection object is created in Login.m which makes the call to the server and then add delegate methods in Login to handle if there's an error or if it's done, these are called by ServerConnection like below.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if( [self.delegate respondsToSelector:#selector(connectionDidFinish:)]) {
NSLog(#"DOES RESPOND");
[self.delegate connectionDidFinish:self];
} else {
NSLog(#"DOES NOT RESPOND");
}
self.connection = nil;
self.receivedData = nil;
}
It always "does not respond". I've tried the CFRunLoop trick (below) but it still doesn't work.
- (IBAction)processLogin:(id)sender {
// Hide the keyboard
[sender resignFirstResponder];
// Start new thread
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Acutally call the server
[self authenticate];
// Prevent the thread from exploding before we've got the data
CFRunLoopRun();
// End thread
[pool release];
}
I copied the Apple URLCache demo pretty heavily and have compared them both many times but can't find any discrepancies.
Any help would be greatly appreciated.

Here are the questions to ask:
Does your delegate respond to connectionDidFinishLoading:?
Does the signature match, i.e. it takes another object?
Is the delegate set at all or is it nil? (Check this in that very method)
If any of those are "NO", you will see "doesn't respond"... and all equally likely to happen in your application, but all are easy to figure out.

Related

Why aren't library (delegate) methods getting invoked? CocoaAsyncSocket

I have been stuck on this issue for a few days now and seem to be getting closer to a resolution (with help from the great users # SO). I'm using the CocoaAsyncSocket library to create a TCP socket connection to a Windows server.
The connection is being made in my appDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
socket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *error = nil;
if (![socket connectToHost:#"199.5.83.63" onPort:11005 error:&error])
{
NSLog(#"Error connecting: %#", error);
}
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[tekMatrixViewController alloc] initWithNibName:#"tekMatrixViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
I have a connect method (part of the library) called onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port:
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(#"connected :D");
}
This method is being called, as I can see the output from NSLOG and I am successfully connected. I can also see the connection is successful from the Windows machine (using log files).
I have also tested that another delegate method is being called as well:
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(#"error - disconnecting");
// start reconnecting procedure here...
}
I have tested that the willDisconnectWithError method works by running my app in the simulator and then unplugging my ethernet cord from my laptop. Once I did this, I saw in my output the "error - disconnecting" string.
The big problem is, however, is that my delegate method (again, from the library) is not being invoked.
Delegate method not being invoked:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if(msg)
{
NSLog(#"RX:%#",msg);
}
else
{
NSLog(#"Fail");
}
}
I'm confident in my knowledge and understanding of how delegate methods work, but I still don't quite follow HOW they're INVOKED. To further complicate and cause confusion, one delegate method (onSocket: didConnectToHost port:) is being invoked, but the other (onSocket: didReadData tag:) is not being invoked. Unfortunately, this is only step one of my problem, but I'll have to get this issue fixed up before I get to the other issue.
Any help would be greatly appreciated. Thanks :D
From the AsyncSocket.h file:
/**
* Called when a socket has completed reading the requested data into memory.
* Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
My first guess is that you have an error and that's why your delegate method is not being called. Did you also implement the delegate method for handling errors?
/**
* In the event of an error, the socket is closed.
* You may call "unreadData" during this call-back to get the last bit of data off the socket.
* When connecting, this delegate method may be called
* before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:".
**/
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
NOTE: Just because you are able to connect without error, doesn't mean that you are going to be able to read without an error occurring. The error argument that you pass into connectToHost:onPort:error: does not cover all error conditions.
EDIT: Could you post the portion of code where you call one of the "readData" methods on the socket? There may be something that is being overlooked there. If for example, no "readData" message is being sent to the socket, then that would explain why your delegate method is not being called.
EDIT: The onSocket:didReadData:withTag: method will only be called after you have invoked one of the following readData methods on the socket. For example:
// The readData and writeData methods won't block (they are asynchronous).
//
// When a read is complete the onSocket:didReadData:withTag: delegate method is called.
// When a write is complete the onSocket:didWriteDataWithTag: delegate method is called.
//
// You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.)
// If a read/write opertion times out, the corresponding "onSocket:shouldTimeout..." delegate method
// is called to optionally allow you to extend the timeout.
// Upon a timeout, the "onSocket:willDisconnectWithError:" method is called, followed by "onSocketDidDisconnect".
//
// The tag is for your convenience.
// You can use it as an array index, step number, state id, pointer, etc.
/**
* Reads the first available bytes that become available on the socket.
*
* If the timeout value is negative, the read operation will not use a timeout.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
I ran into a similar issue with the didReadData() method in my client app not being called after my server app sent set data via the [newSocket writeData:welcomData withTimout:-1 tag:1].
My problem was resolved once I inserted the following line within the didConnectToHost() method. [clientAsyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1.0 tag:0];
Now my client's didReadData() method is called appropriately whenever content is sent its way.

Memory management with NSThread

I have an app that needs to signal continuously a word in morse code. I did this by creating an NSThread and running some code inside the selector with a "while loop". Here is the code:
#implementation MorseCode
-(void)startContinuousMorseBroadcast:(NSString *)words{
if (!(threadIsOn)) {
threadIsOn = YES; s
myThread = [[NSThread alloc] initWithTarget:self selector:#selector(threadSelector:) object:words];
[myThread start];
}
if (morseIsOn) {
morseIsOn = NO;
}
else{
morseIsOn = YES;
}
}
-(void)threadSelector:(NSString *)words{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
while (![myThread isCancelled]) {
// ///it Does some code here
} //end While
NSLog(#"Cleaning the pool");
[pool drain];
}
#end
When exiting the application (the user presses the button), in the applicationDidEnterBackground the following selector is executed:
-(void)cleanUpMorseObject{ //this is defined in the MorseCode class, same as threadSelector
if (threadIsOn) {
NSLog(#"cleanUpMorseObject, threadIsOn");
threadIsOn = NO;
morseIsOn = NO;
[myThread cancel];
[myThread release];
}
}
The application responds correctly to the event, I’ve checked with nslog.
And then [MorseCode release] is called.
The code looks like this:
-(void)applicationDidEnterBackground{ //this happens in the ViewController
[theMorse cleanUpMorseObject]; //theMorse is an instance of MorseCode
[theMorse release];
}
The problem: Although I call [myThread release] and then [theMorse release] the retainCount of the theMorse is still above 0 (It doesn’t call the dealloc).
The Leaks Instrument doesn’t say I have a leak, but if I open and close the application for like 10 times eventually the Iphone resets. Also in the debugger eventually I see the “Received memory warning. Level=2”.
Also I never see the NSLog before the pool drain…
The app doesn't run in the background.
Any ideas? Thank you
You really should schedule the sending of the message on the RunLoop, the probably easiest way being to schedule a timer (repeat infinitely, and short repeat period like FLT_EPSILON or similar) instead of using threads for that.
Working with threads is complicated and as everyone should avoid it (as Apple stated in its Concurrency Programming Guide, and as most documentation said, "Threads are evil" ;)).
That's because multithreading is a vast and complicated subject, that needs synchronizations, resources protection, being aware of dead locks, critical sections & so on, good and adapted memory mgmt, and much much more. In general if you need to do stuff in the background:
Use mechanisms already in place (like asynchronous implementation of some operations and being signalled by delegate methods or notifications) if available
Use methods like performInBackground:
Use NSOperationQueues
Use GCD
And only in last resort and if there are no other options (or for really specific cases), use NSThread.
This will avoid you a lot of issues as all the other, higher APIs will take care of a lot of things for you.
Moreover, using threads for this task like you do is likely to use much more CPU (will probably reach 100% usage quickly) as there won't be any time left for the task scheduler (that also why even GCD that takes care of all stuff like that is way better than NSThreads, and scheduling the sending in the RunLoop is even better for the CPU if you don't need strong RT constraints)
First, retainCount can never return 0. It is a useless method. Don't call it.
Secondly, leaks only detects objects that are no longer referenced. If a thread is still running, it isn't leaked.
Finally, a thread doesn't stop when you call cancel. It just sets a flag that you have to check via isCancelled to see if it is time to stop work in the thread. Are you doing that?
OK -- easy stuff answered. Next? Try build and analyze. Then use the Allocations instrument and turn on reference count tracking. Then see what is calling retain an extra time.
I decided to give up the NSThread class and used another aproach:
-(void)playSOSMorse{
if ([myTimer isValid]) {
[myTimer invalidate];
[myTimer release];
myTimer = nil;
}
myTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001
target:self
selector:#selector(tymerSelector)
userInfo:nil
repeats:NO] retain];
//the timer calls a selector that performs a selector in background
}
-(void)tymerSelector{
[self performSelectorInBackground:#selector(threadSelector2) withObject:nil];
}
-(void)threadSelector2 {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//some code here
[pool drain];
//calls another selector on the main thread to see if it needs to fire the timer again and restart the cycle
[self performSelectorOnMainThread:#selector(selectorOnMainThread) withObject:nil waitUntilDone:NO];
}
-(void)selectorOnMainThread{
[myTimer invalidate];
[myTimer release];
myTimer = nil;
if (morseIsOn) { //this is a boolean that if it is true (YES) calls the timer again
[self playSOSMorse];
}
}
I hope this helps somebody :)
Thank you

UIProgressView update fail with UINotification

I try to solve this problem for several days now I have to ask you...
I've got a View (and a ViewController) with a UITableview. There is a TableViewController for that table which is generated in the ViewController. The TableViewController calls a DataSyncManager sharedInstant object (which is obviously in a separate class) which starts to sync data with the server.
I do it this way (first the refresh method):
-(void) refresh{
[serverQueueProgressView setProgress:0.0];
[syncingLabel setAlpha:0.5];
[serverQueueProgressView setAlpha:1];
[self performSelector:#selector(reloadTableViewDataSource) withObject:nil afterDelay:1.0];
}
Then the method reloadTableViewDataSource (of TableViewController) is called:
- (void)reloadTableViewDataSource
{
[dataSyncManager getEntriesFromServer];
}
dataSyncManager is my sharedInstance.
In the getEntriesFromServer method of dataSyncManager I do the loop with different sync items and call everytime
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ServerQueueProgress"
object:progress];
with the proper progress as NSNumber (that part works well). The message is now sent and catched by my ViewController (it works, I checked with a breakpoint, it also gets the right progress-NSNumber and converts it to float):
- (void)serverQueueProgress:(NSNotification *)notification {
if(![NSThread isMainThread])
{
[self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
return;
}
[queueProgressView setProgress:[[notification object] floatValue]];
}
This is one solution which I found here on stackoverflow. But the if is always skipped because obviously I'm on main thread.
Unfortunately the UIProgressview doesn't get updated, it just hangs around, but I connected it well in Interface Builder (I checked that by setting the progress in another method of ViewController.
I also tried to catch the Notification with my TableViewController and put in some other solutions, but no chance, the UIProgressView doesn't get updated live. Only after the sync is done.
Here is the mentioned code in TableViewController which also gets executed without errors (I also stepped it to make sure every line gehts executed well):
This is the method called when received a the notification:
- (void)serverQueueProgress:(NSNotification *)notification {
[self performSelectorOnMainThread:#selector(updateProgress:) withObject:[notification object] waitUntilDone:NO];
[serverQueueProgressView setProgress:[[notification object] floatValue]];
}
Which also calls updateProgress: of the same class:
- (void)updateProgress:(NSNumber *)newProgressValue {
[serverQueueProgressView setProgress:[newProgressValue floatValue]];
}
No chance. I tried many ways and implemented some in parallel as you see, but the ProgressView won't get updated live. Only at the end of syncing. What am I doing wrong??
EDIT: Here is my getEntriesFromServer and some other stuff in DataSyncManager:
- (void)getEntriesFromServer
{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"SynchingStarted"
object:nil];
[self completeServerQueue];
...
}
and completeServerQueue is the function which sends messages to my ViewController with the proper progress float value (it's only a dummy for loop, which gets executed properly... I've checked it):
- (void)completeServerQueue {
NSNumber *progress = [[NSNumber alloc] init];
for (int i=0; i<15; i++) {
progress = [[NSNumber alloc] initWithFloat:(100/15*i) ];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ServerQueueProgress"
object:progress];
sleep(1);
}
}
also, when you're having trouble, break the problem down a bit. Instead of:
[serverQueueProgressView setProgress:[[notification object] floatValue]];
do this;
float prog = [notification object] floatValue];
[serverQueueProgressView setProgress:prog];
then debugging would give a clue that this isn't working.
my guess is the problem isn't the code you've shown here, but other code in getEntriesFromServer. Are you using NSURLConnection? Something like:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
then you will get callbacks asynchronously that you can use to update your progress view.

How to get/set a global variable (BOOL) from within a background-thread? (Xcode)

From my main thread, I launch an image loader method method-A (below). The problem is, if method-A is not finished at the time a new method-A call is made, image loading starts from the beginning.
What I want to do is, nullify any new method-A calls that are made while a previous method-A call is still doing work... The way I (attempt to) do it now is having a simple global BOOL variable (BOOL imageLoaderBusy) and using it to keep track if the method-A is still working or not (as shown below).
The problem is, the variable seems to be ignored sometimes, and new method-A calls are undesirably started...I dunno. Maybe there is a special way you need to create global variables to make them accessible / valid across multiple threads?
Can somebody please tell me what I am doing wrong? Thanks.
//Method-A called like this:
[self performSelectorInBackground:#selector(loadPagesWithGraphics:) withObject:nil];
//Method-A
-(IBAction)loadPagesWithGraphics:(id)sender{
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
if(!imageLoaderBusy){
imageLoaderBusy = YES;
// Load Images
}
imageLoaderBusy = NO;
[arPool release];
}
Thanks in advance.
Regardless of a variable being an instance variable or a global variable, if multiple threads may write to that variable concurrently, you need to lock that section of code. For instance,
-(IBAction)loadPagesWithGraphics:(id)sender{
#synchronized(self) {
if (imageLoaderBusy) return;
imageLoaderBusy = YES;
}
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
imageLoaderBusy = NO;
[arPool release];
}
Let’s say two executions of that method happen simultaneously in threads A and B, and A gets the lock first, so thread B waits for the lock to be released. From A’s perspective, imageLoaderBusy == NO so it doesn’t return, sets imageLoaderBusy = YES, and releases the lock.
Since the lock has been released, thread B can start executing. It checks imageLoaderBusy and, since thread A has set it to YES, the method returns immediately in thread B.
Thread A proceeds to load the images and sets imageLoaderBusy to NO.
Note that this means that if the method is called again in some thread it will be executed and load the images again. I’m not sure if that’s your intended behaviour; if it’s not, you’ll need another check to determine if images have already been loaded. For instance,
-(IBAction)loadPagesWithGraphics:(id)sender{
if (imagesHaveBeenLoaded) return;
#synchronized(self) {
if (imageLoaderBusy) return;
imageLoaderBusy = YES;
}
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
[arPool release];
imageLoaderBusy = NO; // not strictly necessary
imagesHaveBeenLoaded = YES;
}
You don’t need to have all the method inside a #synchronize block. In fact, critical sections should usually be kept small, especially if the lock is being applied to the whole object (self). If the entire method were a critical section, thread B would have to wait until all images are loaded before noticing that another thread was already busy/had already loaded the images.
Try to change this way:
-(IBAction)loadPagesWithGraphics:(id)sender{
if( imagesDidLoad ) return;
#synchronized(self) {
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
[arPool release];
//set global ivar
imagesDidLoad = YES;
}
}
and in Method-A
add
-(void) methodA {
if( !imagesDidLoad )
[self performSelectorInBackground:#selector(loadPagesWithGraphics:) withObject:nil];
}
in Method-a call a setter on you're main thread to set that BOOL.
The method to do that is : - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

NSOperations or NSThread for bursts of smaller tasks that continuously cancel each other?

I would like to see if I can make a "search as you type" implementation, against a web service, that is optimized enough for it to run on an iPhone.
The idea is that the user starts typing a word; "Foo", after each new letter I wait XXX ms. to see if they type another letter, if they don't, I call the web service using the word as a parameter.
The web service call and the subsequent parsing of the result I would like to move to a different thread.
I have written a simple SearchWebService class, it has only one public method:
- (void) searchFor:(NSString*) str;
This method tests if a search is already in progress (the user has had a XXX ms. delay in their typing) and subsequently stops that search and starts a new one. When a result is ready a delegate method is called:
- (NSArray*) resultsReady;
I can't figure out how to get this functionality 'threaded'.
If I keep spawning new threads each time a user has a XXX ms. delay in the typing I end up in a bad spot with many threads, especially because I don't need any other search, but the last one.
Instead of spawning threads continuously, I have tried keeping one thread running in the background all the time by:
- (void) keepRunning {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SearchWebService *searchObj = [[SearchWebService alloc] init];
[[NSRunLoop currentRunLoop] run]; //keeps it alive
[searchObj release];
[pool release];
}
But I can't figure out how to access the "searchFor" method in the "searchObj" object, so the above code works and keeps running. I just can't message the searchObj or retrieve the resultReady objects?
Hope someone could point me in the right direction, threading is giving me grief:)
Thank you.
Ok, I spend the last 8 hours reading up on every example out there.
I came to realize that I would have to do some "Proof of Concept" code to see if there even would be a speed problem with building a new thread for "each" keystroke.
It turns out that using NSOperation and NSOperationQueue is more than adequate, both in terms of speed and especially in terms of simplicity and abstraction.
Is called after each keystroke:
- (void) searchFieldChanged:(UITextField*) textField {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *searchString = textField.text;
if ([searchString length] > 0) {
[self performSelector:#selector(doSearch:) withObject:textField.text afterDelay:0.8f];
}
}
This is mainly to stop the code form initiating a search for keystrokes that are less than 800 ms. apart.
(I would have that a lot lower if it where not for the small touch keyboard).
If it is allowed to time out, it is time to search.
- (void) doSearch:(NSString*) searchString {
[queue cancelAllOperations];
ISSearchOperation *searchOperation = [[ISSearchOperation alloc] initWithSearchTerm:searchString];
[queue addOperation:searchOperation];
[searchOperation release];
}
Cancel all operations that is currently in the queue. This is called every time a new search is
started, it makes sure that the search operation already in progress gets closed down in an orderly fashion, it also makes sure that only 1 thread is ever in a "not-cancelled" state.
The implementation for the ISSearchOperation is really simple:
#implementation ISSearchOperation
- (void) dealloc {
[searchTerm release];
[JSONresult release];
[parsedResult release];
[super dealloc];
}
- (id) initWithSearchTerm:(NSString*) searchString {
if (self = [super init]) {
[self setSearchTerm:searchString];
}
return self;
}
- (void) main {
if ([self isCancelled]) return;
[self setJSONresult:/*do webservice call synchronously*/];
if ([self isCancelled]) return;
[self setParsedResult:/*parse JSON result*/];
if ([self isCancelled]) return;
[self performSelectorOnMainThread:#selector(searchDataReady:) withObject:self.parsedResult waitUntilDone:YES];
}
#end
There are two major steps, the downloading of the data from the web service and the parsing.
After each I check to see if the search has been canceled by [NSOperationQueue cancelAllOperations] if it has, then we return and the object is nicely cleaned up in the dealloc method.
I will probably have to build in some sort of time out for both the web service and the parsing, to prevent the queue from choking on a KIA object.
But for now this is actually lightning fast, in my test I am searching an 16.000 entries dictionary and having Xcode NSLog it to the screen (slows things down nicely), each 800 ms. I issue a new search string via a timer and thereby canceling the old before it has finished its NSLog results to screen loop.
NSOperationQueue handles this with no glitches and never more that a few ms. of two threads being executed. The UI is completely unaffected by the above tasks running in the background.