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



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值