多线程方案
创建线程一般不应超过5条(包括本来存在的主线程)即非主线程不应超过4条NSThread1、创建线程
// 方式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.NSBlockOperation3.自定义子类继承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 *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];