Objective-C Programming_ The Big Nerd Ranch Guide - Aaron Hillegass [55]
startImmediately:YES];
__unused NSTimer *timer
= [NSTimer scheduledTimerWithTimeInterval:2.0
target:logger
selector:@selector(sayOuch:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
return 0;
}
The instance of Logger will need an instance of NSMutableData to hold onto the bytes as they arrive. Add an instance variable to Logger.h:
#import @interface Logger : NSObject { NSMutableData *incomingData; } - (void)sayOuch:(NSTimer *)t; @end Now implement some of the delegate methods in Logger.m: #import "Logger.h" @implementation Logger - (void)sayOuch:(NSTimer *)t { NSLog(@"Ouch!"); } // Called each time a chunk of data arrives - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"received %lu bytes", [data length]); // Create a mutable data if it doesn't already exist if (!incomingData) { incomingData = [[NSMutableData alloc] init]; } [incomingData appendData:data]; } // Called when the last chunk has been processed - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"Got it all!"); NSString *string = [[NSString alloc] initWithData:incomingData encoding:NSUTF8StringEncoding]; incomingData = nil; NSLog(@"string has %lu characters", [string length]); // Uncomment the next line to see the entire fetched file // NSLog(@"The whole string is %@", string); } // Called if the fetch fails - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"connection failed: %@", [error localizedDescription]); incomingData = nil; } @end Notice that you didn’t implement all the methods in the protocol – just the ones that you cared about. Build and run the program. You should see that the data comes from the web server in reasonable sized chunks. Eventually, the delegate gets informed that the fetch is complete. Here are the rules, so far, for callbacks: When sending one callback to one object, Apple uses target-action. When sending an assortment of callbacks to one object, Apple uses a helper object with a protocol. (We’ll talk more about protocols in the next chapter.) These helper objects are typically called delegate or data source. What if the callback needs to go to multiple objects? Notifications In main.m, register the instance of Logger to receive a notification when the time zone changes: #import #import "Logger.h" int main (int argc, const char * argv[]) { @autoreleasepool { Logger *logger = [[Logger alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:logger selector:@selector(zoneChange:) name:NSSystemTimeZoneDidChangeNotification object:nil]; NSURL *url = [NSURL URLWithString: @"http://www.gutenberg.org/cache/epub/205/pg205.txt"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; __unused NSURLConnection *fetchConn = [[NSURLConnection alloc] initWithRequest:request delegate:logger startImmediately:YES]; __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:logger selector:@selector(sayOuch:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] run]; } return 0; } Now implement the method that will get called in Logger.m: - (void)zoneChange:(NSNotification *)note { NSLog(@"The system time zone has changed!"); } Build and run the program. While it is running, open System Preferences and change the time zone for your system. You should see that your zoneChange: method gets called. (On some systems, it seems to get called twice. This is not cause for concern.) Which to use?
When the user changes the time zone on a Mac, many objects in your program might want to know that the change has occurred. Each one of them can register as an observer with the notification center. When the time zone is changed, the notification NSSystemTimeZoneDidChangeNotification will be posted to the center, and the center will forward it to all the relevant observers.
In this chapter,