OC语言知识4

本文目录

回到顶部

前言

1.上一讲简单介绍了NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销。我们也可以配合使用NSOperation和NSOperationQueue实现多线程编程,实现步骤大致是这样的:

1> 先将需要执行的操作封装到一个NSOperation对象中

2> 然后将NSOperation对象添加到NSOperationQueue中

3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行

在此过程中,我们根本不用考虑线程的生命周期、同步、加锁等问题

下面列举一个应用场景,比如微博的粉丝列表:

每一行的头像肯定要从新浪服务器下载图片后才能显示的,而且是需要异步下载。这时候你就可以把每一行的图片下载操作封装到一个NSOperation对象中,上面有6行,所以要创建6个NSOperation对象,然后添加到NSOperationQueue中,分别下载不同的图片,下载完毕后,回到对应的行将图片显示出来。

2.默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种:

1> NSInvocationOperation

2> NSBlockOperation

3> 自定义子类继承NSOperation,实现内部相应的方法

这讲先介绍如何用NSOperation封装一个操作,后面再结合NSOperationQueue来使用。

回到顶部

一、NSInvocationOperation

1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease];
2 [operation start];

* 第1行初始化了一个NSInvocationOperation对象,它是基于一个对象和selector来创建操作

* 第2行调用了start方法,紧接着会马上执行封装好的操作,也就是会调用self的run:方法,并且将@"mj"作为方法参数

* 这里要注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将operation放到一个NSOperationQueue中,才会异步执行操作。

回到顶部

二、NSBlockOperation

1.同步执行一个操作

1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
2         NSLog(@"执行了一个新的操作");
3 }];
4  // 开始执行任务
5 [operation start];

* 第1行初始化了一个NSBlockOperation对象,它是用一个Block来封装需要执行的操作

* 第2行调用了start方法,紧接着会马上执行Block中的内容

* 这里还是在当前线程同步执行操作,并没有异步执行

2.并发执行多个操作

 1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
 2   NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
 3 }];
 4
 5 [operation addExecutionBlock:^() {
 6   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
 7 }];
 8
 9 [operation addExecutionBlock:^() {
10   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
11 }];
12
13 [operation addExecutionBlock:^() {
14   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
15 }];
16
17 // 开始执行任务
18 [operation start];

* 第1行初始化了一个NSBlockOperation对象

* 分别在第5、9、13行通过addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封装了4个操作

* 在第18行调用start方法后,就会并发地执行这4个操作,也就是会在不同线程中执行

1 2013-02-02 21:38:46.102 thread[4602:c07] 又执行了1个新的操作,线程:<NSThread: 0x7121d50>{name = (null), num = 1}
2 2013-02-02 21:38:46.102 thread[4602:3f03] 又执行了1个新的操作,线程:<NSThread: 0x742e1d0>{name = (null), num = 5}
3 2013-02-02 21:38:46.102 thread[4602:1b03] 执行第1次操作,线程:<NSThread: 0x742de50>{name = (null), num = 3}
4 2013-02-02 21:38:46.102 thread[4602:1303] 又执行了1个新的操作,线程:<NSThread: 0x7157bf0>{name = (null), num = 4}

可以看出,每个操作所在线程的num值都不一样,说明是不同线程

回到顶部

三、NSOperation的其他用法

1.取消操作

operation开始执行之后, 默认会一直执行操作直到完成,我们也可以调用cancel方法中途取消操作

[operation cancel];

2.在操作完成后做一些事情

如果想在一个NSOperation执行完毕后做一些事情,就调用NSOperation的setCompletionBlock方法来设置想做的事情

operation.completionBlock = ^() {
    NSLog(@"执行完毕");
};

当operation封装的操作执行完毕后,就会回调Block里面的内容

回到顶部

四、自定义NSOperation

如果NSInvocationOperation和NSBlockOperation不能满足需求,我们可以直接新建子类继承NSOperation,并添加任何需要执行的操作。如果只是简单地自定义NSOperation,只需要重载-(void)main这个方法,在这个方法里面添加需要执行的操作。

下面写个子类DownloadOperation来下载图片

1.继承NSOperation,重写main方法

DownloadOperation.h

#import <Foundation/Foundation.h>
@protocol DownloadOperationDelegate;

@interface DownloadOperation : NSOperation
// 图片的url路径
@property (nonatomic, copy) NSString *imageUrl;
// 代理
@property (nonatomic, assign) id<DownloadOperationDelegate> delegate;

- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate;
@end

// 图片下载的协议
@protocol DownloadOperationDelegate <NSObject>
- (void)downloadFinishWithImage:(UIImage *)image;
@end

DownloadOperation.m

 1 #import "DownloadOperation.h"
 2
 3 @implementation DownloadOperation
 4 @synthesize delegate = _delegate;
 5 @synthesize imageUrl = _imageUrl;
 6
 7 // 初始化
 8 - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate {
 9     if (self = [super init]) {
10         self.imageUrl = url;
11         self.delegate = delegate;
12     }
13     return self;
14 }
15 // 释放内存
16 - (void)dealloc {
17     [super dealloc];
18     [_imageUrl release];
19 }
20
21 // 执行主任务
22 - (void)main {
23     // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
24     @autoreleasepool {
25         // ....
26     }
27 }
28 @end

* 在第22行重载了main方法,等会就把下载图片的代码写到这个方法中

* 如果这个DownloadOperation是在异步线程中执行操作,也就是说main方法在异步线程调用,那么将无法访问主线程的自动释放池,所以在第24行创建了一个属于当前线程的自动释放池

2.正确响应取消事件

* 默认情况下,一个NSOperation开始执行之后,会一直执行任务到结束,就比如上面的DownloadOperation,默认会执行完main方法中的所有代码。

* NSOperation提供了一个cancel方法,可以取消当前的操作。

* 如果是自定义NSOperation的话,需要手动处理这个取消事件。比如,一旦调用了cancel方法,应该马上终止main方法的执行,并及时回收一些资源。

* 处理取消事件的具体做法是:在main方法中定期地调用isCancelled方法检测操作是否已经被取消,也就是说是否调用了cancel方法,如果返回YES,表示已取消,则立即让main方法返回。

* 以下地方可能需要调用isCancelled方法:

  • 在执行任何实际的工作之前,也就是在main方法的开头。因为取消可能发生在任何时候,甚至在operation执行之前。
  • 执行了一段耗时的操作之后也需要检测操作是否已经被取消

 1 - (void)main {
 2     // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
 3     @autoreleasepool {
 4         if (self.isCancelled) return;
 5
 6         // 获取图片数据
 7         NSURL *url = [NSURL URLWithString:self.imageUrl];
 8         NSData *imageData = [NSData dataWithContentsOfURL:url];
 9
10         if (self.isCancelled) {
11             url = nil;
12             imageData = nil;
13             return;
14         }
15
16         // 初始化图片
17         UIImage *image = [UIImage imageWithData:imageData];
18
19         if (self.isCancelled) {
20             image = nil;
21             return;
22         }
23
24         if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) {
25             // 把图片数据传回到主线程
26             [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];
27         }
28     }
29 }

* 在第4行main方法的开头就先判断operation有没有被取消。如果被取消了,那就没有必要往下执行了

* 经过第8行下载图片后,在第10行也需要判断操作有没有被取消

* 总之,执行了一段比较耗时的操作之后,都需要判断操作有没有被取消

* 图片下载完毕后,在第26行将图片数据传递给了代理(delegate)对象

时间: 2024-10-05 10:39:49

OC语言知识4的相关文章

OC语言知识3

本文目录 知识回顾 一.自定义构造方法 二.description方法 说明:这个Objective-C专题,是学习iOS开发的前奏,也为了让有面向对象语言开发经验的程序员,能够快速上手Objective-C.如果你还没有编程经验,或者对Objective-C.iOS开发不感兴趣,请忽略.学习本专题之前,建议先学习C语言专题. 回到顶部 知识回顾 在第5讲中已经介绍了如何定义类和创建并初始化对象,比如有Student这个类 1.Student.h 1 #import <Foundation/Fo

OC语言知识2

本文目录 一.Java中的this只能用在动态方法中,不能用在静态方法中 二.OC中的self既可以用在动态方法中,也可以用在静态方法中 说明:这个Objective-C专题,是学习iOS开发的前奏,也为了让有面向对象语言开发经验的程序员,能够快速上手Objective-C.如果你还没有编程经验,或者对Objective-C.iOS开发不感兴趣,请忽略.学习本专题之前,建议先学习C语言专题. OC中有一个self关键字,用法跟Java中的this类似,都是代表着当前方法的调用者.但self和th

OC语言知识1

本文目录 一.什么是空指针和野指针 二.野指针和空指针例子 说明:这个Objective-C专题,是学习iOS开发的前奏,也为了让有面向对象语言开发经验的程序员,能够快速上手Objective-C.如果你还没有编程经验,或者对Objective-C.iOS开发不感兴趣,请忽略.学习本专题之前,建议先学习C语言专题. 回到顶部 一.什么是空指针和野指针 1.空指针 1> 没有存储任何内存地址的指针就称为空指针(NULL指针) 2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0.

OC语言知识5

本文目录 前言 一.获取当前线程 二.获取主线程 三.NSThread的创建 四.暂停当前线程 五.线程的其他操作 六.优缺点 回到顶部 前言 每个iOS应用程序都有个专门用来更新显示UI界面.处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法. iOS中有3种常见的多线程编程方法: 1.NSThread 这种

OC语言知识7

本文目录 前言 一.API简介 二.发送异步的GET请求 回到顶部 前言 云计算 近几年来,云计算是一个非常热门的技术名词,很多专家认为,云计算会改变互联网的技术基础,甚至会影响整个产业的格局.可能还很多人不了解什么是云计算,简单来说,就是把用户的数据(比如文档.照片等).用户需要使用的软件.用户需要搜索的资源都保存在"云端",并不用保存在用户本地.你可以将"云端"看做了一台超级计算机,其实是由无数台大型服务器组成的. 移动应用 现在很多的移动应用也类似于"

OC语言知识6

本文目录 一.异步POST请求 二.NSURLConnection的其他请求方法 上一讲介绍了iOS中的异步GET请求,这讲来看看异步POST请求. 回到顶部 一.异步POST请求 假如请求路径是http://192.168.1.102:8080/MJServer/login,请求参数有2个: username :母鸡 pwd :123 1.POST请求细节分析 要想在iOS中发送一个POST请求,首先要了解POST请求的一些细节: 1> 跟GET请求不一样的是,POST请求的请求参数不是拼接在

OC语言知识9

本文目录 一.Core Animation简介 二.Core Animation的使用步骤 三.CAAnimation 四.CAPropertyAnimation 回到顶部 一.Core Animation简介 * Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍.也就是说,使用少量的代码就可以实现非常强大的功能. * Core Animation可以用在Mac OS X和iOS平台. * 乔帮主在2007年的

OC语言知识10

本文目录 一.自定义层的方法1 二.自定义层的方法2 三.其他 自定义层,其实就是在层上绘图,一共有2种方法,下面详细介绍一下. 回到顶部 一.自定义层的方法1 方法描述:创建一个CALayer的子类,然后覆盖drawInContext:方法,使用Quartz2D API进行绘图 1.创建一个CALayer的子类 2.在.m文件中覆盖drawInContext:方法,在里面绘图 1 @implementation MJLayer 2 3 #pragma mark 绘制一个实心三角形 4 - (v

OC语言知识12

本文目录 一.添加一个简单的图层 二.添加一个显示图片的图层 三.为什么CALayer中使用CGColorRef和CGImageRef这2种数据类型,而不用UIColor和UIImage? 四.UIView和CALayer的选择 五.UIView和CALayer的其他关系 * 上一讲已经说过,UIView内部默认有个CALayer对象(层),通过layer属性可以访问这个层.要注意的是,这个默认的层不允许重新创建,但可以往层里面添加子层 * UIView可以通过addSubview:方法添加子视