iOS多线程的初步研究(四、五、六)--NSTimer--NSURLConnection子线程中运行 ---- NSOperation

iOS多线程的初步研究(四)-- NSTimer

理解run loop后,才能彻底理解NSTimer的实现原理,也就是说NSTimer实际上依赖run loop实现的。

先看看NSTimer的两个常用方法:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo; //生成timer但不执行

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo; //生成timer并且纳入当前线程的run
loop来执行

NSRunLoop与timer有关方法为:

- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode; //在run loop上注册timer

主线程已经有run loop,所以NSTimer一般在主线程上运行都不必再调用addTimer:。但在非主线程上运行必须配置run loop,该线程的main方法示例代码如下:

- (void)main

{

  NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES];

  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

  [runLoop addTimer:myTimer forMode:NSDefaultRunLoopMode]; //实际上这步是不需要,scheduledTimerWithTimeInterval已经纳入当前线程运行。如果使用timerWithTimeInterval则需要

  while (condition)

    [runLoop run];

}

实际上这个线程无法退出,因为有timer事件需要处理,[runLoop run]会一直无法返回。解决办法就是设置一个截止时间:

[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //每隔10秒检查下线程循环条件,当然时间值可以根据实际情况来定。

 

我们通常在主线程中使用NSTimer,有个实际遇到的问题需要注意。当滑动界面时,系统为了更好地处理UI事件和滚动显示,主线程runloop会暂时停止处理一些其它事件,这时主线程中运行的NSTimer就会被暂停。解决办法就是改变NSTimer运行的mode(mode可以看成事件类型),不使用缺省的NSDefaultRunLoopMode,而是改用NSRunLoopCommonModes,这样主线程就会继续处理NSTimer事件了。具体代码如下:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

大家可以参看博文http://bluevt.org/?p=209,加深理解NSTimer和NSRunLoop的关系。

 

以前博文中提到延迟调用的方法,其实就是在当前线程的run loop上注册timer来实现定时运行的。所以如果是在非主线程上使用,一定要有一个run loop。

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;


 五、iOS多线程的初步研究(五)--
如何让NSURLConnection在子线程中运行

可以有两个办法让NSURLConnection在子线程中运行,即将NSURLConnection加入到run loop或者NSOperationQueue中去运行。

前面提到可以将NSTimer手动加入NSRunLoop,Cocoa库也为其它一些类提供了可以手动加入NSRunLoop的方法,这些类有NSPort、NSStream、NSURLConnection、NSNetServices,方法都是[scheduleInRunLoop:forMode:]形式。我暂时只介绍下最常用的NSURLConnection类,看看如何把NSURLConnection的网络下载加入到其它线程的run loop去运行。

如果NSURLConnection是在主线程中启动的,实际上它就在主线程中运行 -- 并非启动的另外的线程,但又具备异步运行的特性,这个确实是run loop的巧妙所在。如果对run loop有了初步的了解和概念后,实际上就能明白NSURLConnection的运行,实际也是需要当前线程具备run loop。

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; //将加入指定的run loop中运行,必须保证这时NSURLConnection不能启动,否则不起作用了

- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; //将取消在指定run loop中的运行,实际上就会停止NSURLConnection的运行

下面是如何在其它线程中运行NSURLConnection的主要实现代码:

NSRunLoop *runloop; //global

[self performSelectorInBackground:@selector(thread) withObject:nil]; //启动包含run
loop的线程

NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; //注意这时不能先启动NSURLConnection

[conn scheduleInRunLoop:runloop forMode:NSRunLoopCommonModes]; //指定在上面启动的线程中运行NSURLConnection

[conn start]; //启动NSURLConnection

- (void)thread

{

  runloop = [NSRunLoop currentRunLoop]; //设置为当前线程的run loop值

  while (condition)

  {

    [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
//启动run loop

  }

}

 

将NSURLConnection加入到NSOperationQueue中去运行的方式基本类似:

NSOperationQueue *queue = [[NSOperationQueuealloc] init];

NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 

[conn setDelegateQueue:queue];

[conn start];

六、iOS多线程的初步研究(六)-- NSOperation

iOS平台提供更高级的并发(异步)调用接口,让你可以集中精力去设计需完成的任务代码,避免去写与程序逻辑无关的线程生成、运行等管理代码。当然实质上是这些接口隐含生成线程和管理线程的运行,从而更加简洁地实现多线程。下面先来研究NSOperation和NSOperationQueue类的使用。

NSOperation实质是封装了需要并发运行的代码,一些主要接口和NSThread基本相同,可以看做没有线程运行能力的thread类的抽象。参考NSThread,NSOperation的一些相同的接口有:

- (void)start; //在当前任务状态和依赖关系合适的情况下,启动NSOperation的main方法任务,需要注意缺省实现只是在当前线程运行。如果需要并发执行,子类必须重写这个方法,并且使 - (BOOL)isConcurrent 方法返回YES

- (void)main; //定义NSOperation的主要任务代码

- (BOOL)isCancelled; //当前任务状态是否已标记为取消

- (void)cancel; //取消当前NSOperation任务,实质是标记isCancelled状态

- (BOOL)isExecuting; //NSOperation任务是否在运行

- (BOOL)isFinished; //NSOperation任务是否已结束

NSOperation其它常用方法,包括依赖关系:

- (BOOL)isReady; //是否能准备运行,这个值和任务的依赖关系相关

- (void)addDependency:(NSOperation *)op; //加上任务的依赖,也就是说依赖的任务都完成后,才能执行当前任务

- (void)removeDependency:(NSOperation *)op; //取消任务的依赖,依赖的任务关系不会自动消除,必须调用该方法

- (NSArray *)dependencies; //得到所有依赖的NSOperation任务

以及用于任务同步:

- (void)waitUntilFinished; //阻塞当前线程,直到该NSOperation结束。可用于线程执行顺序的同步

- (void)setCompletionBlock:(void (^)(void))block; //设置NSOperation结束后运行的block代码,由于NSOperation有可能被取消,所以这个block运行的代码应该和NSOperation的核心任务无关。

 

除了继承NSOperation来实现并发代码,通常更简便的办法是使用它的两个子类NSInvocationOperationNSBlockOperation,然后加入到NSOperationQueue执行队列中去运行。部分示例代码如下:

NSInvocationOperation *opA = [[NSInvocationOperationalloc] initWithTarget:self selector:@selector(operate) object:nil];

NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{

        [self operate];

    }];

- (void)operate

{

  //thread loop

  while (condition)

  {

    //....

  }

}

NSOperationQueue *queue = [[NSOperationQueuealloc] init];

queue.maxConcurrentOperationCount = 2; //设置最大并发执行数,如果为1则同时只有一个并发任务在运行,可控制顺序执行关系

[queue addOperation:opA]; //加入到执行队列中,如果isReady则开始执行

[queue addOperation:opB]; //同上,需要注意这时opA和opB是在并发运行

[queue waitUntilAllOperationsAreFinished]; //当前线程等待,直到opA和opB都执行结束

如果要求opB在opA执行完成后才开始执行,需要加上依赖关系即可:

[opB addDependency:opA];

当然也可以使用同步方法waitUntilFinished,在前面的例子中加入:

NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{

   [opA waitUntilFinished]; //opB线程等待直到opA执行结束(正常结束或被取消)

        [self operate];

    }];

建立依赖关系或等待关系,一定要出现避免循环依赖或循环等待,否则就会造成线程死锁。

最后看看NSOperationQueue的其它常用方法:

- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; //批量加入执行operation,wait标志是否当前线程等待所有operation结束后,才返回

- (void)addOperationWithBlock:(void (^)(void))block; //相当于加入一个NSBlockOperation执行任务

- (NSArray *)operations; //返回已加入执行operation的数组,当某个operation结束后会自动从这个数组清除

- (NSUInteger)operationCount //返回已加入执行operation的数目

- (void)setSuspended:(BOOL)b; //是否暂停将要执行的operation,但不会暂停已开始的operation

- (BOOL)isSuspended; //返回暂停标志

- (void)cancelAllOperations; //取消所有operation的执行,实质是调用各个operation的cancel方法

+ (id)currentQueue; //返回当前NSOperationQueue,如果当前线程不是在NSOperationQueue上运行则返回nil

+ (id)mainQueue; //返回主线程的NSOperationQueue,缺省总是有一个queue

时间: 2024-09-18 11:27:56

iOS多线程的初步研究(四、五、六)--NSTimer--NSURLConnection子线程中运行 ---- NSOperation的相关文章

iOS多线程的初步研究(一、二、三)-- NSThread -- 锁-- NSRunLoop

一)iOS多线程的初步研究(一)-- NSThread 对于多线程的开发,iOS系统提供了多种不同的接口,先谈谈iOS多线程最基础方面的使用.产生线程的方式姑且分两类,一类是显式调用,另一类是隐式调用. 一.显示调用的类为NSThread.一般构造NSThread的线程对象可通过两种方式: 1. 初始化线程主方法: [NSThread detachNewThreadSelector:@selector(run:) toTarget:target withObject:obj];//类方法 或 N

iOS多线程的初步研究(七、八、九)-- dispatch对象-- dispatch队列-- dispatch源-- dispatch同步

七.iOS多线程的初步研究()-- dispatch对象 谈起iOS的dispatch(正式称谓是Grand Central Dispatch或GCD),不得不说这又是iOS(包括MacOSX)平台的创新,优缺点这里不讨论,只有当你使用时才能真正体会到.我们说dispatch函数的主要目的是实现多任务并发代码,那么要理解dispatch函数,先来了解dispatch对象的定义.   dispatch对象类型的部分定义,主要使用C语言的宏定义: <os/object.h>文件: #define

iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别?

问题描述 iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别? iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别? 解决方案 那就是异步和同步的问题咯,,异步的话是不会等待请求完成能继续执行下面的程序,,而同步会等待请求的完成,在继续执行下面的 解决方案二: 同步放子线程效果跟异步达到的类似.只是一个是API原生支持,一个是你自己代码来实现 解决方案三: 同步放子线程效果跟异步达到的类似.只是一个是API原生支持,一个是你自己代码来实现 解决方案四: 同步放子线程效果跟

多线程-c#如何在子线程中获取form主线程中按钮点击事件

问题描述 c#如何在子线程中获取form主线程中按钮点击事件 如何在子线程中获取form主线程中按钮点击事件,子线程B中定义了一个新类classnew,获取点击事件也是在新类classnew中,并且获取完点击事件以后执行此类中下边的任务. 解决方案 你可以主线程中得到点击事件后,发送一个Event事件通知等告诉子线程,然后子线程就可以进行后面的任务处理 解决方案二: 不知道你说的获取事件是什么意思,是事件挂钩还是获得挂钩的事件处理函数的委托.请你说清楚. 解决方案三: 主线程中得到点击事件后,发

iOS多线程介绍_java

一.前言部分 最近在面试,重新温习了一遍多线程,希望加深一遍对于多线程的理解. 1.什么是进程? 1).要了解线程我们必须先了解进程,通俗来讲进程就是在系统中运行的一个应用程序. 2).每个线程之间是独立存在的,分别运行在其专用的且受保护的内存空间中. 3).比如打开QQ或Xcode系统会分别开启两个进程 如图: 4).我们可以通过"活动监视器"查看Mac系统中所开启的进程. 2.什么是线程? 1).一个进程要想执行任务必须得有线程,即一个进程至少要有一个线程. 2).线程是进程的基本

iOS多线程开发系列之(三)Grand Central Dispatch(GCD)

上两篇介绍了NSThread和NSOperation的用法,这篇是对第三种多线程开发GCD的介绍 介绍: GCD是苹果公司在iOS4+以后推出的多线程技术,也是苹果着力推荐的,由于基于C语言开发的,所以它的高效性无与伦比的,但学习难度可能在其他两个之上. 创建串行队列 使用dispatch_queue_create函数 使用主队列:使用dispatch_get_main_queue()获得主队列 dispatch_queue_t dispatch_queue_create(const char

理解iOS多线程应用的开发以及线程的创建方法_IOS

一.进程和线程1.什么是进程   进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过"活动监视器"可以查看Mac系统中所开启的进程   2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程的串行   1个线程

iOS多线程开发——NSThread浅析_IOS

在iOS开发中,多线程的实现方式主要有三种,NSThread.NSOperation和GCD,我前面博客中对NSOperation和GCD有了较为详细的实现,为了学习的完整性,今天我们主要从代码层面来实现NSThread的使用.案例代码上传至 https://github.com/chenyufeng1991/NSThread. (1)初始化并启动一个线程 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated

上半年 我省初步建成脱贫攻坚“六有”大数据平台

上半年,我省初步建成脱贫攻坚"六有"大数据平台,11501个贫困村产业脱贫规划编制完成,数百亿扶贫资金下达-- "加上政府补助,自己只需补3万元."7月10日,看着正加紧装修的新房,凉山州喜德县贺波洛乡跃进村贫困村民罗洪古作满脸喜悦,"新房是钢架房,在保留民族特色风格基础上摒弃传统修建材料,修建成本从15万元降到10万元."新房旁,养有50头猪的生猪圈舍已落成数月,跟着大户学养殖,再加上外出务工,罗洪古作今年能多挣1万多元,脱贫在望. 罗洪古作生