每一个应用程序至少有一个主线程。线程的工作就是去执行一系列的指令。在
Cocoa Touch
中,主线程包含应用程序的主运行回路。几乎所有你写的代码都会在主线程中执行,除非你特别创建了一个单独的线程,并在这个新线程中执行代码。
线程有两个显著的特征:
1. 每个线程都有访问你的应用程序资源的同等权限;它包括访问除了局部变量之外的所有的对象。所以,任何对象都可能被任意线程修改,使用并且改变。
2. 没有办法可以去预测一个线程会运行多久 — 或者哪个线程会首先完成!
关于自定义 NSOperation 类,可以遵循以下步骤:
1. 继承 NSOperation 类
2. 重写 “main” 方法
3. 在 “main” 方法中创建一个 “autoreleasepool”
4. 将你的代码放在 “autoreleasepool” 中
创建你自己的自动释放池的原因是,你不能访问主线程的自动释放池,所以你应该自己创建一个。
一、使用通知进行线程之间的通信
@interface ZTDownloadOperation : NSOperation
@property ( nonatomic , strong )UIImage *image;
@property ( nonatomic , copy )NSString *urlString;
@end
@implementation ZTDownloadOperation
-( void )main
{
// 为了防止子线程中的对象不能及时释放 , 手动添加 autoreleasepool.
@autoreleasepool {
// 下载图片 .
self .image = [ self downloadWebImageWithUrlString: self .urlString];
dispatch_async(dispatch_get_main_queue(), ^{
// 需求 : 利用通知将 下载操作 传递出去 .
[[NSNotificationCenter defaultCenter] postNotificationName: @"downloadOperation" object: self ];
});
}
}
// 下载图片
- (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
NSLog( @"downloadWebImageWithUrlString%@" ,[NSThread currentThread]);
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
return image;
}
( 2 )主控制器中;
@interface ViewController ()
@property (nonatomic ,strong) NSOperationQueue *queue;
// 在 storyboard 中拖拽一个 imageView
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
-(NSOperationQueue *)queue
{
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
- ( void )viewDidLoad {
[ super viewDidLoad];
// 添加通知的观察者
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (setUpImageWithNotify:) name: @"downloadOperation" object: nil ];
}
-( void )setUpImageWithNotify:(NSNotification *)notify
{
SHDownloadOperation *op = notify.object;
self .imageView.image = op.image;
}
-( void )dealloc
{
// 移除通知观察者 .
[[NSNotificationCenter defaultCenter] removeObserver: self ];
}
- ( void )touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog( @"touchesBegan" );
// 1. 创建自定义操作 .
SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
// 告诉操作下载哪一张图片 .
op.urlString = @"http://..." ;
// 2. 执行操作
[ self .queue addOperation:op];
}
( 3 ) 通知使用注意 :
通知效率是最低的 . 通知是最灵活最简单 . 两个对象没有联系 , 利用通知来传值 .
使用通知的时候 , 一定要注意移除通知观察者 .
通知是同步的 , 注意线程 .
二、使用代理进行线程之间的通信
主体框架与上述代码基本相似,需要修改的是:
( 1 ) 在自定义 NSOperation .h 文件中,添加代理协议及代理属性
@protocol SHDownloadOperationDelegate <NSObject>
@optional
-( void )downloadWebImageWithImage:(UIImage *)downloadImage;
@end
( 2 ) 在自定义 NSOperation .m 文件中,修改回到主线程函数中的代码
// 实现代理
if ([ self .delegate respondsToSelector: @selector (downloadWebImageWithImage:)]) {
[ self .delegate downloadWebImageWithImage: self .image];
}
( 3 ) 在主控制器中设置代理,并实现代理方法
// 实现代理方法
-( void )downloadWebImageWithImage:(UIImage *)downloadImage
{
NSLog( @" 代理方法 :%@" ,[NSThread currentThread]);
self .imageView.image = downloadImage;
}
三、使用 block 进行线程之间的通信
( 1 ) 在自定义 NSOperation .h 文件中
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// 1. 定义 block 类型
typedef void (^downloadBlock)(UIImage *image);
@interface SHDownloadOperation : NSOperation
// 下载好的图片 .
@property ( nonatomic , strong )UIImage *image;
// 图片的下载地址
@property ( nonatomic , copy )NSString *urlString;
// 定义 block 属性
@property ( nonatomic , copy ) downloadBlock block;
@end
( 2 ) 在自定义 NSOperation .m 文件中 , 修改回到主线程函数中的代码
// 3. 执行 block
if ( self .block) {
NSLog( @"2. 执行 block 中的内容 ." );
self .block( self .image);
}
( 3 ) 在主控制器中
- ( void )touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 1. 创建自定义操作 .
SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
// 设置图片 url 地址
op.urlString = @"http://..." ;
// 2. 定义 block 中执行的内容 .
// block 参数 image : 就是下载好的图片 .
op.block = ^(UIImage *image){
self .imageView.image = image;
NSLog( @"3. 最后执行这块代码 ." );
};
// 2. 执行操作
[ self .queue addOperation:op];
}
线程有两个显著的特征:
1. 每个线程都有访问你的应用程序资源的同等权限;它包括访问除了局部变量之外的所有的对象。所以,任何对象都可能被任意线程修改,使用并且改变。
2. 没有办法可以去预测一个线程会运行多久 — 或者哪个线程会首先完成!
关于自定义 NSOperation 类,可以遵循以下步骤:
1. 继承 NSOperation 类
2. 重写 “main” 方法
3. 在 “main” 方法中创建一个 “autoreleasepool”
4. 将你的代码放在 “autoreleasepool” 中
创建你自己的自动释放池的原因是,你不能访问主线程的自动释放池,所以你应该自己创建一个。
一、使用通知进行线程之间的通信
(1)自定义 NSOperation 文件中;
@interface ZTDownloadOperation : NSOperation
@property ( nonatomic , strong )UIImage *image;
@property ( nonatomic , copy )NSString *urlString;
@end
@implementation ZTDownloadOperation
-( void )main
{
// 为了防止子线程中的对象不能及时释放 , 手动添加 autoreleasepool.
@autoreleasepool {
// 下载图片 .
self .image = [ self downloadWebImageWithUrlString: self .urlString];
dispatch_async(dispatch_get_main_queue(), ^{
// 需求 : 利用通知将 下载操作 传递出去 .
[[NSNotificationCenter defaultCenter] postNotificationName: @"downloadOperation" object: self ];
});
}
}
// 下载图片
- (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
NSLog( @"downloadWebImageWithUrlString%@" ,[NSThread currentThread]);
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
return image;
}
( 2 )主控制器中;
@interface ViewController ()
@property (nonatomic ,strong) NSOperationQueue *queue;
// 在 storyboard 中拖拽一个 imageView
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
-(NSOperationQueue *)queue
{
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
- ( void )viewDidLoad {
[ super viewDidLoad];
// 添加通知的观察者
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (setUpImageWithNotify:) name: @"downloadOperation" object: nil ];
}
-( void )setUpImageWithNotify:(NSNotification *)notify
{
SHDownloadOperation *op = notify.object;
self .imageView.image = op.image;
}
-( void )dealloc
{
// 移除通知观察者 .
[[NSNotificationCenter defaultCenter] removeObserver: self ];
}
- ( void )touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog( @"touchesBegan" );
// 1. 创建自定义操作 .
SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
// 告诉操作下载哪一张图片 .
op.urlString = @"http://..." ;
// 2. 执行操作
[ self .queue addOperation:op];
}
( 3 ) 通知使用注意 :
通知效率是最低的 . 通知是最灵活最简单 . 两个对象没有联系 , 利用通知来传值 .
使用通知的时候 , 一定要注意移除通知观察者 .
通知是同步的 , 注意线程 .
二、使用代理进行线程之间的通信
主体框架与上述代码基本相似,需要修改的是:
( 1 ) 在自定义 NSOperation .h 文件中,添加代理协议及代理属性
@protocol SHDownloadOperationDelegate <NSObject>
@optional
-( void )downloadWebImageWithImage:(UIImage *)downloadImage;
@end
( 2 ) 在自定义 NSOperation .m 文件中,修改回到主线程函数中的代码
// 实现代理
if ([ self .delegate respondsToSelector: @selector (downloadWebImageWithImage:)]) {
[ self .delegate downloadWebImageWithImage: self .image];
}
( 3 ) 在主控制器中设置代理,并实现代理方法
// 实现代理方法
-( void )downloadWebImageWithImage:(UIImage *)downloadImage
{
NSLog( @" 代理方法 :%@" ,[NSThread currentThread]);
self .imageView.image = downloadImage;
}
三、使用 block 进行线程之间的通信
( 1 ) 在自定义 NSOperation .h 文件中
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// 1. 定义 block 类型
typedef void (^downloadBlock)(UIImage *image);
@interface SHDownloadOperation : NSOperation
// 下载好的图片 .
@property ( nonatomic , strong )UIImage *image;
// 图片的下载地址
@property ( nonatomic , copy )NSString *urlString;
// 定义 block 属性
@property ( nonatomic , copy ) downloadBlock block;
@end
( 2 ) 在自定义 NSOperation .m 文件中 , 修改回到主线程函数中的代码
// 3. 执行 block
if ( self .block) {
NSLog( @"2. 执行 block 中的内容 ." );
self .block( self .image);
}
( 3 ) 在主控制器中
- ( void )touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 1. 创建自定义操作 .
SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
// 设置图片 url 地址
op.urlString = @"http://..." ;
// 2. 定义 block 中执行的内容 .
// block 参数 image : 就是下载好的图片 .
op.block = ^(UIImage *image){
self .imageView.image = image;
NSLog( @"3. 最后执行这块代码 ." );
};
// 2. 执行操作
[ self .queue addOperation:op];
}
总的来说,通知的效率最低,一般当两个对象没有联系时,利用通知进行传值;block使用起来最为方便,所以block在工作中使用最为广泛.