Brief Intro to Concurrency and Threading of Foundation Framework

来源:互联网 发布:企业宣传制作软件 编辑:程序博客网 时间:2024/04/30 13:32

当第一次看到NSRunLoop时候,有些不理解,可以结合这个帖子看(NSRunLoop的进一步理解) 有帮助 ^..^

http://blog.csdn.net/diyagoanyhacker/article/details/6783802


Concurrency and Threading

The concurrency and threading support classes implement functionality that both manage threads and support the concurrent execution of multiple sections of code using threads. The following paragraphs provide a general introduction to these classes, refer to Chapter 17 for an in-depth guide to concurrent programming with the Objective-C platform.

Thread Management

The NSTask and NSThread classes are used to manage threads and processes. NSTask enables the creation and management of processes within the Objective-C runtime. An NSTask instance operates as a separate process that does not share memory space with another process, including the process that created it. An NSTask object can only be run once and its environment is configured prior to the task being started. The launchedTaskWithLaunchPath:arguments: method is used to create and start a new task that executes the program namedgreeting in the current directory, as shown in the following statement.

NSTask *hello = [NSTask launchTaskWithLaunchPath:@"./greeting"                                       arguments:nil];

The init method can be used to create a new task; subsequently, the methods setLaunchPath:setArguments:,(if the method has arguments) and launch would then be used to start the task.

NSTask *hello = [[NSTask alloc] init];[hello setLaunchPath:@"./greeting"];[hello launch];

The isRunning method can be used to query the state of a task, as shown in the following statement.

BOOL running = [hello isRunning];

A thread is an operating system mechanism that enables the concurrent execution of multiple sequences of instructions. A thread is typically implemented as a lightweight process, in that multiple threads can exist concurrently within the same process. Threads within a process can share computer memory and other resources. NSThread is used to create and control threads. The class includes methods to create and initialize anNSThread object, start and stop a thread, configure a thread, and query a thread and its execution environment. The NSThread class methoddetachNewThreadSelector:toTarget:withObject: is used to create and start a new thread. The following statement uses this method to create and start a new thread on the execute: method of an object named processEngine, with an input parameter of myData.

NSThread *myThread = [NSThread detachNewThreadSelector:@selector(execute:)                                              toTarget:processEngine                                            withObject:myData];

The thread priority can be set with the setThreadPriority: method, as shown in the following statement which sets the thread myThread to apriority of 1.0, the highest priority.

[myThread setThreadPriority:1.0];

A thread can be signaled to cancel with cancel method; the following statement sends this message to the thread myThread.

[myThread cancel];

Concurrent Operations

NSOperationNSBLockOperation, and NSInvocationOperation are used to manage concurrent execution of one or more operations, code, and data associated with a single task. An operation queue is an Objective-C object that provides the ability to execute tasks concurrently. Each task (i.e., operation) defines the work to be performed, along with its associated data, and is encapsulated in either a block object or a concrete subclass of NSOperationNSOperation is an abstract class that encapsulates the code and data associated with a single task. For nonconcurrent tasks, a concrete subclass typically only needs to override the main method. For concurrent tasks, you must override at a minimum the methods startisConcurrentisExecuting, and isFinished. Listing 11-8 provides an implementation of the classGreetingOperation, a concrete subclass of NSOperation that logs a simple greeting to the output pane.

Listing 11-8.  GreetingOperation Class

@interface GreetingOperation : NSOperation@end@implementation GreetingOperation- (void)main{  NSLog(@"Hello, world!");}@end

NSOperationQueue controls the execution of NSOperation objects through a queuing system. Operation queues may employ threads to execute their operations; however, this implementation detail is hidden, thereby simplifying application development and reducing the potential for errors. Listing 11-9 creates an instance of a GreetingOperation, and then submits it to an NSOperationQueue to execute concurrently.

Listing 11-9.  Submitting GreetingOperation Instance to a Queue

NSOperation *greetingOp = [[GreetingOperation alloc] init];[[NSOperationQueue mainQueue] addOperation:greetingOp];

Operation queues provide a simpler, more efficient mechanism for implementing concurrency and are therefore recommended in lieu ofthreads for implementing concurrent programming.

Locking

NSLockNSDistributedLockNSConditionLock, and NSRecursiveLockare used to create locks for synchronizing code execution. NSLockimplements a basic mutual exclusion (mutex) lock for concurrent programming. It conforms to the NSLocking protocol and thus implements the lock and unlock methods to acquire and release a lock accordingly.

The NSDistributedLock class defines a lock that can be used by multiple applications on multiple hosts to control access to a shared resource.

The NSConditionLock class defines a lock that can be acquired and released only under certain conditions.

The NSRecursiveLock class defines a lock that can be acquired multiple times by the same thread without causing deadlock. It keeps track of the number of times it was acquired, and must be balanced by corresponding calls to unlock the object before the lock is released.

Timers and Run Loops  

run loop is a thread-based mechanism used to schedule work and coordinate the receipt of input events. If a program thread needs to respond to incoming events, you need to attach it to a run loop in order to wake up this thread when new events arrive. The run method of the iOS UIApplication class (or NSApplication in OS X) starts an application’s main loop as part of the normal startup sequence; hence, if you use the Xcode template projects for creating iOS or OS X programs, you typically won’t need to create a run loop in your code. In other scenarios—when creating a command-line program, for example—you need to create a run loop if your program needs to respond to events from input sources. The Foundation Framework includes numerous classes that provide inputs asynchronously (for example, the networking, URL handling, and stream I/O APIs), so you would use a run loop in conjunction with instances of these classes.

The NSRunLoop class provides the API for managing run loops. This class includes methods to access run loops and modes, manage timers and ports, run a loop, and manage messages. The NSRunLoopcurrentRunLoop method retrieves the current run loop, the NSRunLoopobject for the current thread. If a run loop doesn’t already exist for the thread, it is created and returned. The following statement retrieves the run loop for the current thread.

NSRunLoop *loop = [NSRunLoop currentRunLoop];

The run mode defines the set of input sources for a run loop. The Foundation Framework defines a set of constants that specify the available run loop modes. The constant NSDefaultRunLoopMode is the most commonly used run-loop mode.

There are several NSRunLoop methods used to run a loop. The runmethod puts the thread in a permanent loop in theNSDefaultRunLoopMode, during which it processes events from all input sources. If you want a run loop to terminate, you shouldn’t use this method, but rather one of the other methods used to conditionally run a loop based on receipt of input and/or a specified amount of time has elapsed.

The runMode:beforeDate: method runs the loop once in theNSDefaultRunLoopMode, blocking for input until an event is received from an attached input source or the date specified by the beforeDate:parameter arrives. The following statement invokes this method, waiting indefinitely until input is received.

[loop runMode: NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

Providing a date parameter value of [NSDate distantFuture] evaluates to an infinite wait until input is received.

The NSTimer class is used to create timers that send a specified message to a target object after a certain time has elapsed. They work in conjunction with NSRunLoop objects to provide synchronous delivery of events to a thread. An NSTimer determines the maximum amount of time a run loop object should wait for input. NSTimer includes methods to create and initialize a timer, fire a timer, stop a timer, and retrieve information about a timer. ThescheduledTimerWithTimeInterval:invocation:repeats: method creates a timer that fires after the specified interval, using the invocation specified, and repeating the timer if requested. The invalidate method stops a timer from firing again. The isValid method returns a Boolean value indicating whether or not the timer is currently valid.

Creating a Bonjour Network Service Client

Now you’re going to create a program that demonstrates the use of the Bonjour network services APIs and run loops. This program will create a service browser that asynchronously looks for registered services of specified types.

In Xcode, create a new project by selecting New image Project . . . from the Xcode File menu. In the New Project Assistant pane, create a command-line application. In the Project Options window, specifyBonjourClient for the Product Name, choose Foundation for the Project Type, and select ARC memory management by selecting the Use Automatic Reference Counting check box. Specify the location in your file system where you want the project to be created (if necessary, selectNew Folder and enter the name and location for the folder), uncheck the Source Control check box, and then click the Create button.

Now you’re going to create a class that downloads a URL resource using the URL loading APIs. Select New image File . . . from the Xcode File menu, then select the Objective-C class template, and name the classBonjourClient. Then select the BonjourClient folder for the files location and the BonjourClient project as the target, and then click theCreate button. Next, in the Xcode project navigator pane, select the resulting header file named BonjourClient.h and update the interface, as shown in Listing 11-10.

Listing 11-10.  BonjourClient Interface

#import <Foundation/Foundation.h>@interface BonjourClient : NSObject <NSNetServiceBrowserDelegate>@property (retain) NSNetServiceBrowser *serviceBrowser;@property BOOL finishedLoading;@end

The BonjourClient interface adopts the NSNetServiceBrowserDelegateprotocol, thereby enabling it to asynchronously load data from anNSNetServiceBrowser instance. The serviceBrowser property is theNSNetServiceBrowser instance. The finishedLoading property is used to indicate when the NSNetServiceBrowser instance is finished loading data. Now select the BonjourClient.m file and update the implementation, as shown in Listing 11-11.

Listing 11-11.  BonjourClient Implementation

#import "BonjourClient.h"@implementation BonjourClient- (id)init{  if ((self = [super init]))  {    _finishedLoading = NO;    _serviceBrowser = [[NSNetServiceBrowser alloc] init];    [_serviceBrowser setDelegate:self];}  return self;}#pragma mark -#pragma mark NSNetServiceBrowserDelegate methods- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)netServiceBrowser{  NSLog(@"Beginning search");}- (void)netServiceBrowser:(NSNetServiceBrowser *)sb           didFindService:(NSNetService *)ns               moreComing:(BOOL)moreComing{  NSLog(@"Found service: %@", ns);  if (!moreComing)  {    // No more services, stop search    [self.serviceBrowser stop];  }}- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)netServiceBrowser{  // Stopped search, set flag to exit run loop  NSLog(@"Stopped search");  self.finishedLoading = YES;}@end

All right, let’s analyze this code one method at a time. The init method initializes the finishedLoading property to a value of NO to indicate that the BrowserClient instance has not finished browsing for services. It then creates a service browser instance and assigns it to the corresponding property, setting itself as the delegate object. This means that the BrowserClient instance will implement the necessaryNSNetServiceBrowserDelegate protocol methods, thereby responding to asynchronous events when its service browser instance is searching for registered services.

The netServiceBrowserWillSearch: message is sent to the delegate by the service browser instance when it begins searching for services. The implementation here just logs a message to the output pane, indicating that the browser is beginning a search for services.

The netServiceBrowser:didFindService:moreComing: message is sent to the delegate by the service browser instance when it finds a registered service. The method implementation here just logs the method found to the output pane, and then performs a conditional check to see if the service browser is waiting for additional services. If there are no more services available, the browser is sent a stop message with the following statement.

[self.serviceBrowser stop];

The netServiceBrowserDidStopSearch: message is sent to the delegate by the service browser instance when a search for services is stopped. The implementation logs a message to the output pane, indicating that the search was stopped, and then sets thefinishedLoading property to YES.

OK, now that you have implemented the BonjourClient class, let’s use this to search for registered services using the Bonjour protocol.  In the Xcode project navigator, select the main.m file and update the main()function, as shown in Listing 11-12.

Listing 11-12.  BonjourClient main( ) Function

#import <Foundation/Foundation.h>#import "BonjourClient.h"int main(int argc, const char * argv[]){  @autoreleasepool  {    // Retrieve the current run loop for browsing    NSRunLoop *loop = [NSRunLoop currentRunLoop];    // Create a browser client and add its service browser to the current run loop    BonjourClient *client = [[BonjourClient alloc] init];    // Browse for the specified service types    [client.serviceBrowser searchForServicesOfType:@"_ipp._tcp."                                          inDomain:@"local."];    // Loop until the browser is stopped    while (!client.finishedLoading &&           [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);  }  return 0;}

The main() function begins by retrieving the current run loop, required for asynchronous URL loading with NSNetServiceBrowser objects. Next, a BonjourClient instance is created and initialized. Then the service browser begins browsing for registered services using thesearchForServicesOfType:inDomain: method. Here you specify as types services those that use the IPP (internet printing protocol, used for printers) and TCP (transport control protocol) protocols, and are registered in the local domain. The next statement is a run loop used to keep the application running until the service browser has finished searching for registered services.

while (!client.finishedLoading &&       [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

The while loop has a conditional expression composed of two parts, and is followed by a semicolon, signifying an empty statement. Thus there is no logic executed within the body of the while loop; it is used just to keep the application running until the asynchronous search being performed by the service browser has finished. The first part of the conditional expression, !client.finishedLoading, checks the value of this Boolean property. If it is YES, then the while loop is exited; otherwise, you continue on to process the remainder of the expression.

Next, the expression [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] executes the run loop once, blocking for input from an input source (for example, receipt of an NSNetServiceBrowsermessage). Once the input is received, the message is processed on the current thread (i.e., the corresponding delegate method is invoked) and the loop begins again. Thus the while loop will not be exited until thefinishedLoading property is set. As shown in Listing 11-10, this property is set by the BonjourClient delegate methodnetServiceBrowserDidStopSearch:, the matching message is sent by the service browser when it has finished the search.

Earlier in this chapter, you learned that the Bonjour protocol enables the detection of devices (such as computers and printers) connected on a network. Hence, if possible, make sure that you have one or more devices connected to your local area network when you test this program. Once you have performed these configuration steps and compiled and run the BonjourClient program, you should observe messages in the output pane similar to those shown in Figure 11-1.

9781430250500_Fig11-01.jpg

Figure 11-1. BonjourClient program output

As shown in Figure 11-1, the services retrieved are listed in the output pane, along with messages indicating the beginning and end of the search. And that’s it. This hands-on exercise showed you how to write a program that uses the Foundation Framework APIs to asynchronously find registered services. Feel free to take some time to review this program and the key features covered here—using run loops and the Bonjour network protocol. Once you’re done, let’s move on to URL handling!