iOS开发全面解析多线程

iOS学习交流及资料获取:新浪微博 关注➕ 私信极客James

iOS开发全面解析多线程_第1张图片

一.多线程基本概念

1. 进程

进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。

2. 线程

基本概念

1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

线程的串行

1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。

3.多线程

基本概念

即1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务

线程的并行

并行即同时执行。比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C。

多线程并发执行的原理

在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

4.多线程优缺点

优点

1)能适当提高程序的执行效率。

2)能适当提高资源利用率(CPU、内存利用率)

缺点

1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。

2)线程越多,CPU在调度线程上的开销就越大。

3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享

5.注意事项:

(1)不要同时开太多的线程(1~3条线程即可,不要超过5条)

(2) 主线程 : UI线程,显示、刷新UI界面,处理UI控件的事件

(3) 子线程 : 后台线程,异步线程

(4)不要把耗时的操作放在主线程,要放在子线程中执行

二、NSThread

1.创建和启动线程的3种方式

(1)先创建,后启动

// 创建

NSThread *thread = [[NSThread alloc] initWithTarget:self

selector:@selector(download:) object:nil];

// 启动 [thread start];

(2)创建完自动启动

[NSThread detachNewThreadSelector:@selector(download:) toTarget:self

withObject:nil];

(3) 隐式创建(自动启动)

[self performSelectorInBackground:@selector(download:)

withObject:nil];

2.常见方法

(1) 获得当前线程

+ (NSThread *)currentThread;

(2) 获得主线程

+ (NSThread *)mainThread;

(3) 睡眠(暂停)线程

+(void)sleepUntilDate:(NSDate *)date;

+(void)sleepForTimeInterval:(NSTimeInterval)ti;

(4)设置线程的名字

-(void)setName:(NSString *)n;

-(NSString *)name;

线程同步

1.实质:为了防止多个线程抢夺同一个资源造成的数据安全问题

2.实现:给代码加一个互斥锁(同步锁)

@synchronized(self) {

// 被锁住的代码

}

三、GCD

1.队列和任务

(1) 任务 :需要执行什么操作

用block来封装任务

(2) 队列 :存放任务

全局的并发队列 : 可以让任务并发执行

dispatch_queue_t queue =

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

自己创建的串行队列 : 让任务一个接着一个执行

dispatch_queue_t queue = dispatch_queue_create(“cn.heima.queue”,

NULL);

主队列 : 让任务在主线程执行

dispatch_queue_t queue = dispatch_get_main_queue();

2.执行任务的函数

(1) 同步执行 : 不具备开启新线程的能力

dispatch_sync…

(2) 异步执行 : 具备开启新线程的能力

dispatch_async…

3.常见的组合

iOS开发全面解析多线程_第2张图片

(1) 异步函数+并发队列:开启多条线程,并发执行任务

(2) 异步函数+串行队列:开启一条线程,串行执行任务

(3) 同步函数+并发队列:不开线程,串行执行任务

(4) 同步函数+串行队列:不开线程,串行执行任务

(5) 异步函数+主队列:不开线程,在主线程中串行执行任务

(6) 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)

(7) 注意同步函数和异步函数在执行顺序上面的差异

注意事项:同步函数往当前的串行队列中添加任务,会卡住当前的线程,形成死锁。

4.线程间的通信

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,

0), ^{

// 执行耗时的异步操作..

dispatch_async(dispatch_get_main_queue(),

^{

// 回到主线程,执行UI刷新操作 }); });

5.GCD的常用方法

(1)延迟执行

<1> performSelector

需求: 3秒后自动回到当前线程调用self的download:方法,并且传递参数:@”http://555.jpg”

[selfperformSelector:@selector(download:)withObject:@"http://555.jpg"afterDelay:3];

<2> dispatch_after

// 任务放到哪个队列中执行 dispatch_queue_t queue =

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); double

delay = 3;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,

(int64_t)(delay * NSEC_PER_SEC)), queue, ^{

// 3秒后需要执行的任务

});

(2)一次性代码

static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{

// 这里面的代码,在程序运行过程中,永远只会执行1次 });

(3)栅栏函数(控制任务的执行顺序)

dispatch_barrier_async(queue, ^{

NSLog(@"--dispatch_barrier_async-");

});

(4)快速迭代(开多个线程并发完成迭代操作)

dispatch_apply(subpaths.count, queue, ^(size_t index) {

});

(5)队列组(同栅栏函数)

//创建队列组

dispatch_group_t group = dispatch_group_create();

//队列组中的任务执行完毕之后,执行该函数

dispatch_group_notify(dispatch_group_t group,

dispatch_queue_t queue,

dispatch_block_t block);

四、NSOperation和NSOperationQueue

1.基本概念:

NSOperation是对GCD的包装, 两个核心概念【队列+操作】

2.基本使用

(1) NSOperation本身是抽象类,只能只有它的子类

(2)三个子类分别是:NSBlockOperation、NSInvocationOperation以及自定义继承自NSOperation的类

(3) NSOperation和NSOperationQueue结合使用实现多线程并发

3.队列的类型

(1) 主队列

[NSOperationQueue mainQueue]

添加到”主队列”中的操作,都会放到主线程中执行

(2) 非主队列

[[NSOperationQueue alloc] init]

添加到”非主队列”中的操作,都会放到子线程中执行

4.队列添加任务

-(void)addOperation:(NSOperation *)op;

-(void)addOperationWithBlock:(void (^)(void))block;

5.常见用法

(1) 设置最大并发数

-(NSInteger)maxConcurrentOperationCount;-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;

(2) 队列的其他操作

取消所有的操作

-(void)cancelAllOperations;

暂停所有的操作

[queue setSuspended:YES];

恢复所有的操作

[queue setSuspended:NO];

6.操作之间的依赖(面试题)

NSOperation之间可以设置依赖来保证执行顺序

[operationB addDependency:operationA];

// 操作B依赖于操作A,等操作A执行完毕后,才会执行操作B

注意:不能相互依赖,比如A依赖B,B依赖A

可以在不同queue的NSOperation之间创建依赖关系

7.线程之间的通信

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue

addOperationWithBlock:^{

// 1.执行一些比较耗时的操作

// 2.回到主线程

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];

}];

五、单例模式

1.ARC实现单例模式思路

(1) 在类的内部提供一个static修饰的全局变量

(2) 提供一个类方法,方便外界访问

(3) 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间

(4) 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法

(1) 创建一个类工厂方法

@interface DataTool : NSObject

+ (instancetype)sharedDataTool; @end

@implementation DataTool

// 用来保存唯一的单例对象

static id _instace;

// 重写allocWithZone方法

+ (id)allocWithZone:(struct _NSZone *)zone {

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

_instace = [super allocWithZone:zone];

});

return _instace; }

// sharedDataTool方法的实现+ (instancetype)sharedDataTool {staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{        _instace = [[selfalloc] init];    });return_instace; }

// 重写copyWithZone方法- (id)copyWithZone:(NSZone *)zone{return_instace;}@end


2.MRC实现单例的思路

(1) 在类的内部提供一个static修饰的全局变量

(2) 提供一个类方法,方便外界访问

(3) 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间

(4) 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法

(5) 重写release方法

(6) 重写retain方法

(7) 建议在retainCount方法中返回一个最大值

// 声明一个类工厂方法@interfaceDataTool:NSObject+ (instancetype)sharedDataTool;@end


@implementation HMDataTool

// 用来保存唯一的单例对象

static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone {

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

_instace = [super allocWithZone:zone];

});

return _instace; }

+ (instancetype)sharedDataTool{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{        _instace = [[selfalloc] init];    });return_instace;}


// 重写copyWithZone方法

- (id)copyWithZone:(NSZone *)zone {

return _instace; }

// 重写release- (onewayvoid)release {  }


// 重写retain- (id)retain {returnself;}


-(NSUInteger)retainCount {return MAXFLOAT;}


// 重写autorelease- (id)autorelease {returnself;}@end


3.判断编译器的环境:ARC还是MRC的方法

#if__has_feature(objc_arc)// 当前的编译器环境是ARC#else// 当前的编译器环境是MRC#endif


六、从其他线程回到主线程的方式

1.performSelectorOnMainThread

[selfperformSelectorOnMainThread:<#(SEL)#>withObject:<#(id)#>waitUntilDone:<#(BOOL)#>];

2.GCD

dispatch_async(dispatch_get_main_queue(), ^{});


3.NSOperationQueue

[[NSOperationQueue mainQueue]addOperationWithBlock:^{}];

七、类的初始化方法

1.+(void)load

当某个类第一次装载到OC运行时系统(内存)时,就会调用

程序一启动就会调用

程序运行过程中,只会调用1次

2.+(void)initialize

当某个类第一次被使用时(比如调用了类的某个方法),就会调用

并非程序一启动就会调用

3.在程序运行过程中:1个类中的某个操作,只想执行1次,那么这个操作放到+(void)load方法中最合适

八、cell的图片下载

1.面试题

1> 如何防止一个url对应的图片重复下载

“cell下载图片思路 – 有沙盒缓存”

2> SDWebImage的默认缓存时长是多少?

1个星期

3> SDWebImage底层是怎么实现的?

(1)无沙盒缓存

实现步骤:

1.先缓存需要的图片

2.根据图片的url去image中取图片 有图片 直接显示到cell上

3.图片不存在,显示一个占位图片

4.根据图片的url查看operations中是否存在下载操作

5.若下载操作存在,执行正在下载

6.不存在,创建下载操作放到operation中

7.下载完成,将操作从operation中移除,将图片放到image中

8.刷新表格

iOS开发全面解析多线程_第3张图片

(2)有沙盒缓存

1.根据图片的URL去image中取图片 存在 显示到cell上

2.如果图片不存在 检查沙盒中是否有图片 存在 显示到cell上

3.如果不存在 显示一张占位图片

4.根据图片的URL检查operation是否有下载操作 存在 继续下载

5.如果不存在 创建下载操作 放到operation中

6.下载完毕 将操作从operation中移除

7.刷新表格 按行刷新

8.将图片保存到沙盒中

iOS开发全面解析多线程_第4张图片

2.第三方框架SDWebImage的使用

(1) 常用方法

-(void)sd_setImageWithURL:(NSURL)url placeholderImage:(UIImage)placeholder;

-(void)sd_setImageWithURL:(NSURL*)url placeholderImage:(UIImage*)placeholder options:(SDWebImageOptions)options;

-(void)sd_setImageWithURL:(NSURL*)url placeholderImage:(UIImage*)placeholder completed:(SDWebImageCompletionBlock)completedBlock;

-(void)sd_setImageWithURL:(NSURL)url placeholderImage:(UIImage)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock

completed:(SDWebImageCompletionBlock)completedBlock;

(2) 内存处理:当app接收到内存警告时

// 当app接受到内存警告

–(void)applicationDidReceiveMemoryWarning:(UIApplication *)application {

SDWebImageManager *mgr = [SDWebImageManager sharedManager];

// 1.取消正在下载的操作

[mgr cancelAll];

// 2.清除内存缓存

[mgr.imageCache clearMemory]; }

(3) SDWebImageOptions

SDWebImageRetryFailed : 下载失败后,会自动重新下载

SDWebImageLowPriority : 当正在进行UI交互时,自动暂停内部的一些下载操作

SDWebImageRetryFailed | SDWebImageLowPriority : 拥有上面2个功能

你可能感兴趣的:(iOS开发全面解析多线程)