一、多线程概述
1. 程序:由源代码生成的可执行应用。(wechat就是一个程序)
2. 进程:一个正在运行的程序可以看做一个进程。(正在运行的wechat就是一个进程)
3. 线程:程序中独立运行的代码段。(接收wechat消息的代码)
一个进程由一个或多个线程组成。进程只负责调度与分配,线程才是真正的执行单元,负责代码的执行。
4. 单线程:
4.1 每个正在运行的进程,至少包括一个线程,这个线程就是主线程。
4.2 主线程在程序启动时被创建,用于执行main函数。
4.3 只有一个主线程的程序被称为单线程程序。
4.4 主线程负责执行程序的所有代码,这些代码只能顺序执行,无法并发执行。
4.5 UI的展现只能由主线程修改。
5. 多线程:
5.1:拥有多个线程的程序,称为多线程程序。
5.2:iOS允许开辟新的进程, 但是相对于主线程而言,这些线程被称为子线程。
5.3:子线程与主线程都是独立运行的单元,各自的执行互不影响,因此能够并发执行。
6. 单线程与多线程区别:
单线程程序只有一个线程,代码顺序执行,容易出现阻塞。(页面卡住不动)
多线程程序有多个线程,线程间独立运行,避免了阻塞,也提高了程序的运行性能。
eg.模拟主线程阻塞:
创建了一个按钮, 点击触发一个方法,方法计算1加到635500000。
// 创建一个按钮
- (void)createButton
{
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeCustom];
aButton.frame =CGRectMake(100, 100, 150, 50);
[aButton setTitle:@"click" forState:UIControlStateNormal];
[aButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[aButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
[aButton addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:aButton];
}
// 触发事件
- (void)buttonAction:(id)sender
{
NSInteger sum = 0;
for (int i = 1; i <= 635500000; i++) {
sum += i;
NSLog(@"%ld", sum);
}
}
二、iOS多线程实现
1. NSThread
NSTread是一个轻量级的多线程,它有两种创建方法。
注意:在多线程方法中需要添加自动释放池;在程序开始系统会为主线程创建自动释放池;手动创建的子线程需要手动添加释放池。
NSThread主要由两种初始化方法:
// 实例化方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(calculator) object:nil];
[thread start];
[thread release];
// 类方法
[NSThread detachNewThreadSelector:@selector(calculator) toTarget:self withObject:nil];
2. NSOperation & NSOperationQueue
2.1 NSOperation类在MVC中属于model类,是用来封装单个任务相关的代码和数据的抽象类。由于是抽象类,我们不能够直接使用这个类,而是使用子类来执行任务。通常与NSOperationQueue结合使用。
eg.NSOperation与NSOperationQueue结合使用
注意:当最大并发数设置为1的时候,能实现线程同步。
<span style="white-space:pre"> </span>CustomOperation *operation = [[CustomOperation alloc] init];
// 将opration放入queue中时不能start, queue会自动调用它的方法开始;
CustomOperation *operation1 = [[CustomOperation alloc] init];
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
//<span style="white-space:pre"> </span>maxConcurrentOperationCount设置最大并发数量
opQueue.maxConcurrentOperationCount = 1;
[opQueue addOperation:operation];
[opQueue addOperation:operation1];
[operation release];
[operation1 release];
[opQueue release];
NSInvocationOperation是NSOperation的子类。封装了执行操作的target和action。
eg.
NSInvocationOperation *invoOpr = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(calculator) object:nil];
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
[queue1 addOperation:invoOpr];
[invoOpr release];
[queue1 release];
NSBlockOperation是NSOperation的子类。封装了需要执行操作的代码块。
eg.封装了一个输出0~99的代码块
NSBlockOperation *blockOpr = [NSBlockOperation blockOperationWithBlock:^{
@autoreleasepool {
// block内容就是多线程所要执行的代码;
for (int i = 0; i < 100; i++) {
NSLog(@"%d", i);
}
}];
}
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:blockOpr];
[queue release];
3. NSObject实现异步后台执行
NSObject中存在一个最简单的后台执行方法。
eg.
[self performSelectorInBackground:@selector(calculator) withObject:self];
4. GCD(Grand Central Dispatch)
4.1 简介:GCD是苹果公司开发的技术。以优化应用程序支持多核心处理器和其他的对称多处理系统的系统。GCD输入函数级的多线程,性能更高。iOS4以上可用。
GCD以队列的方式工作,先进先出。
GCD中有两种队列:SerialQueue,Concurrent。其中serial一次只执行一个任务。Concurrent可以并发执行多个任务。
4.2 Serial
e.g Serial实例
- (void)createSerialGCD
{
// 第一步: 创建一个同步线程GCD队列;
dispatch_queue_t queue = dispatch_queue_create("first", DISPATCH_QUEUE_SERIAL);
// 第二步: 异步执行同步线程队列
dispatch_async(queue, ^{
// 多线程的代码, 与主线程异步执行;
// GCD中不能使用自动释放池;----C语言
// 子线程不可以对view修改;
// 下载一张图片, 并显示到界面上, (下载用同步下载方式);
NSString *str1 = @"http://g.hiphotos.baidu.com/image/scrop%3D100/sign=94ffc93249fbfbedd807712008cdc606/21a4462309f7905256721f330ef3d7ca7bcbd54a.jpg";
// 中文字符显示
NSString *str2 = [str1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:str2];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"GET"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [UIImage imageWithData:data];
// 显示到界面上, 所有跟UI相关的内容全部都要在主线程运行
// 返回主线程
dispatch_async(dispatch_get_main_queue(), ^{
// 在主线程运行
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 150, 375, 300)];
imgView.image = image;
[self.view addSubview:imgView];
[imgView release];
});
});
}
e.g Concurrent实例
- (void)createConcurrentGCD
{
// global_queue默认并行队列
//DISPATCH_QUEUE_PRIORITY_DEFAULT,有四种级别
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://img.hb.aicdn.com/258541cf3d0b5dc5c68f9f3096a830b68288d2011f65f-00EuvV_fw658"];
// 同步下载
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 350, 60, 60)];
imgView.image = image;
[self.view addSubview:imgView];
[imgView release];
});
});
}
SDWebImage的简单实现
#import "ToolsModel.h"
@implementation ToolsModel
// 获取图片路径
+ (NSString *)libraryPathWithImageName:(NSString *)imageStr
{
NSArray *array = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *rootPath = [array firstObject];
rootPath = [NSString stringWithFormat:@"%@/%@", rootPath, imageStr];
NSLog(@"%@", rootPath);
return rootPath;
}
// 判断文件是否存在
+ (BOOL)imageExist:(NSString *)imageStr
{
NSString *path = [ToolsModel libraryPathWithImageName:imageStr];
NSFileManager *imageManager = [NSFileManager defaultManager];
return [imageManager fileExistsAtPath:path];
}
// 文件存储
+ (void)saveimage:(NSData *)data imageName:(NSString *)imageName
{
NSString *path = [ToolsModel libraryPathWithImageName:imageName];
[data writeToFile:path atomically:YES];
}
// 获取本地文件
+ (NSData *)readimage:(NSString *)imageStr
{
if ([ToolsModel imageExist:imageStr]) {
NSString *path = [ToolsModel libraryPathWithImageName:imageStr];
NSData *data = [NSData dataWithContentsOfFile:path];
return data;
}
return [NSData data];
}
@end
#import "UIImageView+WebCache.h"
#import "ToolsModel.h"
@implementation UIImageView (WebCache)
- (void)setImageWithUrl:(NSString *)urlStr placeholder:(NSString *)placeholder
{
NSString *aStr = [urlStr stringByReplacingOccurrencesOfString:@"/" withString:@"a"];
NSString *bStr = [aStr stringByReplacingOccurrencesOfString:@":" withString:@"b"];
NSString *str = [bStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if (![ToolsModel imageExist:str]) {
NSString *str = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:str];
[self downloadWithUrl:url imageName:str];
} else {
NSData *data = [ToolsModel readimage:str];
UIImage *image = [UIImage imageWithData:data];
self.image = image;
}
}
- (void)downloadWithUrl:(NSURL *)url imageName:(NSString *)imagename
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
[ToolsModel saveimage:data imageName:imagename];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:data];
self.image = image;
});
});
}
@end
#import "ViewController.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self createImageView];
}
- (void)createImageView
{
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 200, 360, 200)];
[imgView setImageWithUrl:@"http://img.hb.aicdn.com/258541cf3d0b5dc5c68f9f3096a830b68288d2011f65f-00EuvV_fw658" placeholder:nil];
[self.view addSubview:imgView];
[imgView release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}