zoukankan      html  css  js  c++  java
  • iPhone多线程开发之线程队列NSOperationQueue与自定义Protocol

    一.多线程对于iPhone应用程序开发很重要

          在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程处理一个常见的例子就是用户界面设计,利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开始响应,这就是所谓的阻塞主线程使得界面被冻结。 特别是在一些基于网络的应用里,多线程技术显得尤为重要。当用户向服务器发出一个请求时,你需要做的有发送请求,接收数据,有时还需要解析数据(xml),但是如果你不是启动一个线程去做这些事情的话,那么做这些动作的时候你的界面将会被“冻结”,给用户造成不友好的体验。所以我们需要多线程技术。

    二.NSOperationQueue

          在这之前,你可能需要了解什么是NSOperation,这在官方文档里面写的很清楚。线程是从NSOperation中的main函数开始执行。类似于java中的多线程机制中的run函数。

    下面让我们来看看NSOperationQueue的方法:

    - (void)addOperation:(NSOperation *)operation

    the specified operation remains in the queue until it finishes executing

    创建一个NSOperationQueue对象,然后创建NSOperation添加到NSOperationQueue中。NSOperationQueue会为每一个NSOperation创建一个线程,并按照它们被添加的顺序执行(或者指定的顺序)。NSOperationQueue会自己处理autorelease pools 和其他的garbage,而不需要像在自己手动创建多线程时去费心管理这些事情,当把NSOperation的对象放进队列中,此线程就会按照先进先出的方式等待运行,并且是从main函数开始运行。

    三.自定义协议与委托机制

          协议就是约定好的一组方法,协议可以实现对象之间的通信,好像是一种通知或者回调机制:“在某件事情发生的时候通知我”,是cocoa设计模式的核心。经常与多线程编程搭配使用,用来通知主线程可以继续执行。

    在下面的例子中我们看看这是怎么实现的:

    首先,LazyTableAppDelegate.m里面

    实现了NSURLConnection的代理,然后异步的等待数据下载结束之后,则从客户端启动另一个线程开始解析数据

    1.ParseOperation *parser = [[ParseOperation alloc] initWithData:appListData delegate:self];

    2.[queue addOperation:parser];

    ParseOperation继承了NSOperation,然后将这个线程放入线程线程队列,应该是一个优先级队列,NSOperationQueue,如果此线程位于队列的头,则这个线程开始执行。线程的任务完成后,要通知主线程完成了相关的工作,那么什么时间返回主进程呢?这就用到了代理。[self.delegate didFinishParsing:self.workingArray] self.delegate代理指向的是是LazyTableAppDelegate的对象,这样就完成了对LazyTableAppDelegate的回调,需要注意的是,在LazyTableAppDelegate里实现了在ParseOperation.h里声明的那个协议,协议的声明方法见下面代码:

    28  @protocol ParseOperationDelegate
    29  - (void)didFinishParsing:(NSArray *)appList;
    30  - (void)parseErrorOccurred:(NSError *)error;
    31 @end

    这里只是声明这些方法,在哪里定义呢,在需要被回调的地方定义,也就是LazyTableAppDelegate。

    看一下回调的方法: [self.delegate didFinishParsing:self.workingArray];

    这个方法是在线程ParseOperation执行结束的时候调用。self.delegate目前是指向的是LazyTableAppDelegate类的对象,这个对象则调用所继承的协议里面声明的方法- (void)didFinishParsing:(NSArray *)appList,这样就完成了回调。如何回到主线程的呢?看看(void)didFinishParsing:(NSArray *)appList这个函数是如何在LazyTableAppDelegate.m里定义的:

    37  - (void)didFinishParsing:(NSArray *)appList
    38 {
    39 [self performSelectorOnMainThread:@selector(handleLoadedApps:) withObject:appList waitUntilDone:NO];
    40 self.queue = nil;
    41 }

    一切真相大白,这个函数里面又调用了一个方法handleLoadedApps,并且使用performSelectorOnMainThread,使之在主线程中执行操作,这时候就可以释放我们的线程池了。一切又回归到主线程中了。

    四.示例代码(官方程序:lazytable)

    LazyTableAppDelegate.h 

    1 #import <UIKit/UIKit.h>
    2 #import "AppRecord.h"
    3 #import "ParseOperation.h"
    4
    5 @class RootViewController;
    6
    7 @interface LazyTableAppDelegate : NSObject <UIApplicationDelegate, ParseOperationDelegate>
    8 {
    9 UIWindow *window;
    10 UINavigationController *navigationController;
    11
    12 // this view controller hosts our table of top paid apps
    13   RootViewController *rootViewController;
    14
    15 // the list of apps shared with "RootViewController"
    16   NSMutableArray *appRecords;
    17
    18 // the queue to run our "ParseOperation"
    19   NSOperationQueue *queue;
    20
    21 // RSS feed network connection to the App Store
    22   NSURLConnection *appListFeedConnection;
    23 NSMutableData *appListData;
    24 }
    25
    26 @property (nonatomic, retain) IBOutlet UIWindow *window;
    27 @property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
    28 @property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
    29 @property (nonatomic, retain) NSMutableArray *appRecords;
    30 @property (nonatomic, retain) NSOperationQueue *queue;
    31 @property (nonatomic, retain) NSURLConnection *appListFeedConnection;
    32 @property (nonatomic, retain) NSMutableData *appListData;
    33
    34 @end
    35  

    LazyTableAppDelegate.m

    1 #import "LazyTableAppDelegate.h"
    2 #import "RootViewController.h"
    3 #import "ParseOperation.h"
    4
    5 #import <CFNetwork/CFNetwork.h>
    6
    7  static NSString *const TopPaidAppsFeed =
    8 @"http://phobos.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/limit=75/xml";
    9
    10
    11 @implementation LazyTableAppDelegate
    12
    13 @synthesize window, navigationController, appRecords, rootViewController, queue, appListFeedConnection, appListData;
    14
    15
    16  #pragma mark -
    17
    18  - (void)applicationDidFinishLaunching:(UIApplication *)application
    19 {
    20 // Configure and show the window
    21   [window addSubview:[self.navigationController view]];
    22 [window makeKeyAndVisible];
    23 // self.appRecords,rootViewController.entries指向同一快内存
    24   self.appRecords = [NSMutableArray array];
    25 rootViewController.entries = self.appRecords;
    26
    27 NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:TopPaidAppsFeed]];
    28 self.appListFeedConnection = [[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self] autorelease];
    29 }
    30
    31  - (void)handleLoadedApps:(NSArray *)loadedApps
    32 {
    33 [self.appRecords addObjectsFromArray:loadedApps];
    34 [rootViewController.tableView reloadData];
    35 }
    36
    37  - (void)didFinishParsing:(NSArray *)appList
    38 {
    39 [self performSelectorOnMainThread:@selector(handleLoadedApps:) withObject:appList waitUntilDone:NO];
    40 self.queue = nil;
    41 }
    42
    43  - (void)parseErrorOccurred:(NSError *)error
    44 {
    45 [self performSelectorOnMainThread:@selector(handleError:) withObject:error waitUntilDone:NO];
    46 }
    47
    48  #pragma mark -
    49 #pragma mark NSURLConnection delegate methods
    50
    51 - (void)handleError:(NSError *)error
    52 {
    53 ...
    54 }
    55
    56 // The following are delegate methods for NSURLConnection. Similar to callback functions, this is how
    57 // the connection object, which is working in the background, can asynchronously communicate back to
    58 // its delegate on the thread from which it was started - in this case, the main thread.
    59 //
    60
    61 // -------------------------------------------------------------------------------
    62 // connection:didReceiveResponse:response
    63 // -------------------------------------------------------------------------------
    64 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    65 {
    66 self.appListData = [NSMutableData data]; // start off with new data
    67 }
    68
    69 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    70 {
    71 [appListData appendData:data]; // append incoming data
    72 }
    73
    74 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    75 {
    76 ...
    77 }
    78
    79 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    80 {
    81 self.appListFeedConnection = nil; // release our connection
    82
    83 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    84
    85 // create the queue to run our ParseOperation
    86 self.queue = [[NSOperationQueue alloc] init];
    87
    88 // create an ParseOperation (NSOperation subclass) to the RSS feed data so that the UI is not blocked
    89 // "ownership of appListData has been transferred to the parse operation and should no longer be
    90 // referenced in this thread.
    91 //
    92 ParseOperation *parser = [[ParseOperation alloc] initWithData:appListData delegate:self];
    93
    94 [queue addOperation:parser]; // this will start the "ParseOperation"
    95
    96 [parser release];
    97
    98 // ownership of appListData has been transferred to the parse operation
    99 // and should no longer be referenced in this thread
    100 self.appListData = nil;
    101 }
    102
    103 - (void)dealloc
    104 {
    105 ...
    106 [window release];
    107 [super dealloc];
    108 }
    109
    110 @end

    ParseOperation.h

    1 @class AppRecord;
    2 @protocol ParseOperationDelegate;
    3
    4 @interface ParseOperation : NSOperation
    5 {
    6 @private
    7 id <ParseOperationDelegate> delegate;
    8
    9 NSData *dataToParse;
    10 NSMutableArray *workingArray;
    11 AppRecord *workingEntry;
    12 NSMutableString *workingPropertyString;
    13 NSArray *elementsToParse;
    14 BOOL storingCharacterData;
    15 }
    16 @property (nonatomic, assign) id <ParseOperationDelegate> delegate;
    17 @property (nonatomic, retain) NSData *dataToParse;
    18 @property (nonatomic, retain) NSMutableArray *workingArray;
    19 @property (nonatomic, retain) AppRecord *workingEntry;
    20 @property (nonatomic, retain) NSMutableString *workingPropertyString;
    21 @property (nonatomic, retain) NSArray *elementsToParse;
    22 @property (nonatomic, assign) BOOL storingCharacterData;
    23  - (id)initWithData:(NSData *)data delegate:(id <ParseOperationDelegate>)theDelegate;
    24
    25 @end
    26
    27  //声明协议
    28  @protocol ParseOperationDelegate
    29  - (void)didFinishParsing:(NSArray *)appList;
    30  - (void)parseErrorOccurred:(NSError *)error;
    31 @end
    32  

    ParseOperation.m

    1 #import "ParseOperation.h"
    2 #import "AppRecord.h"
    3 #import "LazyTableAppDelegate.h"
    4
    5 @implementation ParseOperation
    6
    7 @synthesize delegate, dataToParse, workingArray, workingEntry, workingPropertyString, elementsToParse, storingCharacterData;
    8
    9  - (id)initWithData:(NSData *)data delegate:(id <ParseOperationDelegate>)theDelegate
    10 {
    11 self = [super init];
    12 if (self != nil)
    13 {
    14 self.dataToParse = data;
    15 self.delegate = theDelegate;
    16 self.elementsToParse = [NSArray arrayWithObjects:kIDStr, kNameStr, kImageStr, kArtistStr, nil];
    17 }
    18 return self;
    19 }
    20
    21  - (void)dealloc
    22 {
    23 [dataToParse release];
    24 [workingEntry release];
    25 [workingPropertyString release];
    26 [workingArray release];
    27
    28 [super dealloc];
    29 }
    30
    31
    32  - (void)main
    33 {
    34 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    35
    36 self.workingArray = [NSMutableArray array];
    37 self.workingPropertyString = [NSMutableString string];
    38
    39 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataToParse];
    40 [parser setDelegate:self];
    41 [parser parse];
    42
    43 if (![self isCancelled])
    44 {
    45 [self.delegate didFinishParsing:self.workingArray];
    46 }
    47
    48 self.workingArray = nil;
    49 self.workingPropertyString = nil;
    50 self.dataToParse = nil;
    51
    52 [parser release];
    53 [pool release];
    54 }
    55
    56
    57  #pragma mark -
    58 #pragma mark RSS processing
    59
    60 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
    61 namespaceURI:(NSString *)namespaceURI
    62 qualifiedName:(NSString *)qName
    63 attributes:(NSDictionary *)attributeDict
    64 {
    65 ...
    66 }
    67 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
    68 namespaceURI:(NSString *)namespaceURI
    69 qualifiedName:(NSString *)qName
    70 {
    71 ...
    72 }
    73 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    74 {
    75 ...
    76 }
    77 - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
    78 {
    79 [delegate parseErrorOccurred:parseError];
    80 }
    81 @end
    82
  • 相关阅读:
    Bootstrap-CSS:按钮
    Bootstrap-CSS:表单
    质检总局-版权局
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯最小公倍数(c++)
    java实现第二届蓝桥杯最小公倍数(c++)
  • 原文地址:https://www.cnblogs.com/cherri/p/1895541.html
Copyright © 2011-2022 走看看