博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS开发-多线程知识
阅读量:5959 次
发布时间:2019-06-19

本文共 7772 字,大约阅读时间需要 25 分钟。

多线程方案

创建线程一般不应超过5条(包括本来存在的主线程)即非主线程不应超过4条
NSThread

1、创建线程

// 方式1,创建线程,设置,启动线程,如果调用的方法需要参数,可以用object这个参数传参    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];    thread.name = @"下载东西";    [thread start];    // 方式2,创建线程后自启动线程    [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];        // 方式3,隐式创建并启动线程    [self performSelectorInBackground:@selector(download) withObject:nil];    // 后面两种方式优点在于简单快捷,缺点就是没法做详细设置,如设置name

2、获得当前线程

- (void)download{    // 打印当前所在的线程,会显示name和线程标号,1为主线程,其他为子线程    NSLog(@"---%@---",[NSThread currentThread]);}

3、判断线程是否主线程

+ (NSThread *)mainThread; // 获得主线程- (BOOL)isMainThread; // 是否为主线程+ (BOOL)isMainThread; // 是否为主线程

 

控制线程的方法

// 进入就绪状态 -> 运行状态,当线程任务执行完毕,自动进入死亡状态- (void)start;// 进入阻塞状态1.直到某刻+ (void)sleepUntilDate:(NSDate *)date;2.几秒之后+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 进入死亡状态+ (void)exit;

 

多线程的安全隐患

当多个线程访问同一块资源的时候会出现数据错乱的问题,所以使用多线程会存在安全隐患。

解决方法:使用互斥锁
互斥锁格式:(※锁对象可以使任何对象,但是不要频繁创建,否则浪费资源;※一份代码对应一把锁)

@synchronized(锁对象) { // 需要锁定的代码  }

其实互斥锁的本质就是让线程同步,使线程按顺序执行。

优点:能有效防止因多线程抢夺资源造成的数据安全问题

缺点:需要消耗大量的CPU资源

 

原子和非原子属性

 

原子属性:atomic,为setter方法加锁(默认就是atomic

非原子属性:nonatomic,不会为setter方法加锁

对比:
atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备
建议定义属性都用nonatomic属性,在需要加锁的地方才用互斥锁,这样能节省资源,更加适合移动设备上的开发,并且需要注意的是,加锁和资源抢夺等业务逻辑都交给服务器端去处理。

 

线程间的通信

1.主线程:UI线程,用于显示、刷新UI界面,处理UI控件的事件

2.子线程:后台线程,异步线程,用于耗时操作
如何通信?
1.A线程传递数据给B线程
2.一个线程执行完特定任务后,转到另一个线程执行,如下载网络图片,一般由子线程从网络上下载图片,然后让主线程刷新UIImageView上的图片。

NSOperation

配合使用NSOperation和NSOperationQueue也能实现多线程
实现步骤:
1.先将需要执行的操作封装到一个NSOperation对象中
2.然后将NSOperation对象添加到NSOperationQueue中
3.系统会自动将NSOperationQueue中的NSOperation取出来
4.将取出的NSOperation封装的操作放到一条新线程中执行
但是,NSOperation是一个抽象类,并不具备封装操作的能力,所以我们必须使用它的子类

 使用NSOperation子类的方式有下面3种:

 1.NSInvocationOperation(不常用)
2.NSBlockOperation
3.自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation的基本实现

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];     // operation直接调用start,是同步执行(在当前线程执行,不会另开线程//    [operation start];)    // 要创建一个operation队列然后add进去才能实现多线程    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    [queue addOperation:operation];- (void)download{    NSLog(@"---%@---",[NSThread currentThread]);}

NSBlockOperation的基本实现
没使用队列

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"---下载1---%@",[NSThread currentThread]);    }];        //addExecutionBlock方法可以往operation中添加任务    [operation1 addExecutionBlock:^{        NSLog(@"--下载11---%@",[NSThread currentThread]);    }];        [operation1 addExecutionBlock:^{        NSLog(@"--下载12---%@",[NSThread currentThread]);    }];        // 如果调用start,operation的任务数大于1的话,第1个任务在主线程执行,其他的异步(另开线程)串行执行(按顺序执行)    [operation1 start];

添加到队列

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"---下载1---%@",[NSThread currentThread]);    }];        //addExecutionBlock方法可以往operation中添加任务    [operation1 addExecutionBlock:^{        NSLog(@"--下载11---%@",[NSThread currentThread]);    }];            NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"---下载2---%@",[NSThread currentThread]);    }];        // 放进下面队列的任务都是异步(不同线程)并发执行(同时执行)    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    [queue addOperation:operation1];    [queue addOperation:operation2];

直接使用队列,不用新建NSBlockOperation对象

// 1.创建队列(非主队列)NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.调用addOperationWithBlock方法,直接往队列里面添加任务    [queue addOperationWithBlock:^{        NSLog(@"---下载1---%@",[NSThread currentThread]);    }];    [queue addOperationWithBlock:^{        NSLog(@"---下载2---%@",[NSThread currentThread]);    }];    [queue addOperationWithBlock:^{        NSLog(@"---下载3---%@",[NSThread currentThread]);    }];// 以上都是异步并发执行的

自定义NSOperation

重写- (void)main方法,在里面实现想执行的任务重写- (void)main方法的注意点1.自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)2.经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

 

NSOperationQueue的使用

类型:
1.主队列[NSOperationQueue mainQueue];
2.非主队列[[NSOperationQueue alloc] init];
可以设置线程的最大并发数,设置之后可以优化性能,因为它会重复利用用过的线程

NSOperationQueue *queue = [[NSOperationQueue alloc] init];queue.maxConcurrentOperationCount = 2; //或者 [queue setMaxConcurrentOperationCount:2];

NSOperation的其他使用

>可以通过设置依赖来保证执行顺序,注意两个operation不能互相依赖
>两个不同队列中的operation之间也能设置依赖

/**     假设有A、B、C三个操作,要求:     1. 3个操作都异步执行     2. 执行完A再执行B再执行C     */        // 1.创建一个队列(非主队列)    NSOperationQueue *queue = [[NSOperationQueue alloc] init];        // 2.创建3个操作    NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"A1---%@", [NSThread currentThread]);    }];        NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"B---%@", [NSThread currentThread]);    }];    NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"C---%@", [NSThread currentThread]);    }];        // 设置依赖    [operationB addDependency:operationA];    [operationC addDependency:operationB];        // 3.添加操作到队列中(自动异步执行任务)    [queue addOperation:operationC];    [queue addOperation:operationA];    [queue addOperation:operationB];

>可以为某个operation设置监听,等它执行完后执行想要的代码

NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"A1---%@", [NSThread currentThread]);    }];[operationA setCompletionBlock:^{     NSLog(@"AAAAA---%@", [NSThread currentThread]); }];

>线程间的通信,NSThread,GCD,NSOperation之间可以混合使用

NSOperationQueue *queue = [[NSOperationQueue alloc] init];    [queue addOperationWithBlock:^{        // 1.异步下载图片        NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];        NSData *data = [NSData dataWithContentsOfURL:url];        UIImage *image = [UIImage imageWithData:data];                // 2.回到主线程,显示图片// 2.1 NSThread//        [self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];// 2.2 GCD//        dispatch_async(dispatch_get_main_queue(), ^{//            //        });// 2.3        [[NSOperationQueue mainQueue] addOperationWithBlock:^{            self.imageView.image = image;        }];    }];

>队里的取消,暂停,恢复及一般应用的地方

* 取消所有的操作

- (void)cancelAllOperations;

* 暂停所有的操作

[queue setSuspended:YES];

* 恢复所有的操作

[queue setSuspended:NO];

- (void)didReceiveMemoryWarning{    [super didReceiveMemoryWarning];    //    [queue cancelAllOperations]; // 取消队列中的所有任务(不可恢复)}- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{//    [queue setSuspended:YES]; // 暂停队列中的所有任务}- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{//    [queue setSuspended:NO]; // 恢复队列中的所有任务}

如何避免cell中的图片重复下载(思路)

需要:创建字典images(根据URL(key)存储图片(value))、字典operations(根据URL存储操作)

用第三方框架:SDWebImage即可实现

 

把图片写入沙盒

// UIImage --> NSData --> File    NSData *data = UIImagePNGRepresentation(image);        // 一般存在沙盒中Library的caches文件夹里面,Document和Library中的preferences会影响iTunes的软件同步,而tmp文件夹不安全,里面的数据随时会被系统删除    // 获得caches的文件路径    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, NO)];    // 获得网络图片的URL    NSString *filename = [imageURL lastpathComponent];    // 拼接文件路径    NSString *file = [caches stringByAppendingPathComponent:filename];    [data writeToFile:file atomically:YES];

 

从沙盒中读取图片

// 获得caches的文件路径    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, NO)];    // 获得网络图片的URL    NSString *filename = [imageURL lastpathComponent];    // 拼接文件路径    NSString *file = [caches stringByAppendingPathComponent:filename];        NSData *data = [NSData dataWithContentsOfFile:file];    UIImage *image = [UIImage imageWithData:data];

 

 

 

转载于:https://www.cnblogs.com/jierism/p/6106702.html

你可能感兴趣的文章
为什么 Python 4.0 会与 Python 3.0 不同?
查看>>
Android无处不在,Android开发者大有可为
查看>>
Nodejs:使用Mongodb存储和提供后端CRD服务
查看>>
Dubbo配置直连
查看>>
一个小白的四次前端面试经历
查看>>
Hybrid App技术解析 -- 原理篇
查看>>
前端也要学系列:设计模式之策略模式
查看>>
【译】SQL 指引:如何写出更好的查询
查看>>
细说 Java 的深拷贝和浅拷
查看>>
go配置文件读取
查看>>
通过项目梳理vuex模块化 与vue组件管理
查看>>
每天阅读一个 npm 模块(2)- mem
查看>>
React Native 中的状态栏
查看>>
Python 抓取微信公众号账号信息
查看>>
Spring源码系列:依赖注入-引言
查看>>
在Node.js中使用C++模块
查看>>
Redis持久化RDB和AOF优缺点是什么?
查看>>
iOS-性能优化深入探究
查看>>
阿里云Redis混合存储典型场景:如何轻松搭建视频直播间系统
查看>>
基于阿里云服务搭建的典型技术架构
查看>>