iOS开发多线程篇—自定义NSOperation

简介:

一、实现一个简单的tableView显示效果

实现效果展示:

代码示例(使用以前在主控制器中进行业务处理的方式)

1.新建一个项目,让控制器继承自UITableViewController。

复制代码
 1 //  2 // YYViewController.h
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import <UIKit/UIKit.h>
10 11 @interface YYViewController : UITableViewController
12 13 @end
复制代码

2.处理storyboard中得界面,如下:

3.根据plist文件,字典转模型

新建一个类,继承自NSObject,作为数据的模型

YYappModel.h文件

复制代码
 1 //  2 // YYappModel.h
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import <Foundation/Foundation.h>
10 11 @interface YYappModel : NSObject
12 /**
13  *应用名称
14 */ 15 @property(nonatomic,copy)NSString *name;
16 /**
17  * 应用图片
18 */ 19 @property(nonatomic,copy)NSString *icon;
20 /**
21  * 应用的下载量
22 */ 23 @property(nonatomic,copy)NSString *download;
24 25 +(instancetype)appModelWithDict:(NSDictionary *)dict;
26 -(instancetype)initWithDict:(NSDictionary *)dict;
27 @end
复制代码

YYappModel.m文件

复制代码
 1 //  2 // YYappModel.m
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import "YYappModel.h" 10 11 @implementation YYappModel
12 13 -(instancetype)initWithDict:(NSDictionary *)dict
14 {
15 if (self=[super init]) {
16  [self setValuesForKeysWithDictionary:dict];
17  }
18 return self;
19 }
20 21 //工厂方法 22 +(instancetype)appModelWithDict:(NSDictionary *)dict
23 {
24 return [[self alloc]initWithDict:dict];
25 }
26 @end
复制代码

主控制器中得逻辑控制部分,YYViewController.m文件

复制代码
 1 //  2 // YYViewController.m
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import "YYViewController.h" 10 #import "YYappModel.h" 11 12 @interface YYViewController ()
13 @property(nonatomic,strong)NSArray *apps;
14 15 @end 16 17 @implementation YYViewController
18 #pragma mark- 懒加载
19 -(NSArray *)apps
20 {
21 if (_apps==nil) {
22 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
23 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
24 25 //字典转模型 26 NSMutableArray *array=[NSMutableArray array];
27 for (NSDictionary *dict in tempArray) {
28 YYappModel *app=[YYappModel appModelWithDict:dict];
29  [array addObject:app];
30  }
31 _apps=array;
32  }
33 return _apps;
34 }
35 36 #pragma mark-数据源方法
37 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
38 {
39 return self.apps.count;
40 }
41 42 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
43 {
44 static NSString *ID=@"ID";
45 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
46 if (cell==nil) {
47 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
48  }
49 YYappModel *app=self.apps[indexPath.row];
50 cell.textLabel.text=app.name;
51 cell.detailTextLabel.text=app.download;
52 53 //下载图片数据 54 NSLog(@"加载图片数据---%@", [NSThread currentThread]);
55 NSURL *url=[NSURL URLWithString:app.icon];
56 NSData *data=[NSData dataWithContentsOfURL:url];
57 UIImage *imgae=[UIImage imageWithData:data];
58 cell.imageView.image=imgae;
59 NSLog(@"完成显示");
60 return cell;
61 }
62 63 @end
复制代码

打印查看:

二、自定义NSOperation

说明:上面的下载图片数据部分是一个非常耗时的操作,这个操作任务在主线程完成,会严重的影响到用户体验,造成UI卡的现象。下面通过自定义NSOperation,新开线程,让加载图片的任务异步执行。

1.通过代理

在上面的基础上,新建一个类,让其继承自NSOperation。

YYdownLoadOperation.h文件

复制代码
 1 //  2 // YYdownLoadOperation.h
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import <Foundation/Foundation.h>
10 11 #pragma mark-设置代理和代理方法
12 @class YYdownLoadOperation;
13 @protocol YYdownLoadOperationDelegate <NSObject>
14 -(void)downLoadOperation:(YYdownLoadOperation*)operation didFishedDownLoad:(UIImage *)image;
15 @end 16 17 @interface YYdownLoadOperation : NSOperation
18 @property(nonatomic,copy)NSString *url;
19 @property(nonatomic,strong)NSIndexPath *indexPath;
20 @property(nonatomic,strong)id <YYdownLoadOperationDelegate> delegate;
21 @end
复制代码

YYdownLoadOperation.m文件

复制代码
 1 //  2 // YYdownLoadOperation.m
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import "YYdownLoadOperation.h" 10 11 @implementation YYdownLoadOperation
12 -(void)main
13 {
14 NSURL *url=[NSURL URLWithString:self.url];
15 NSData *data=[NSData dataWithContentsOfURL:url];
16 UIImage *imgae=[UIImage imageWithData:data];
17 18 NSLog(@"--%@--",[NSThread currentThread]);
19 //图片下载完毕后,通知代理 20 if ([self.delegate respondsToSelector:@selector(downLoadOperation:didFishedDownLoad:)]) {
21 dispatch_async(dispatch_get_main_queue(), ^{//回到主线程,传递数据给代理对象 22 [self.delegate downLoadOperation:self didFishedDownLoad:imgae];
23  });
24  }
25 }
26 @end
复制代码

主控制器中的业务逻辑:

复制代码
 1 //  2 // YYViewController.m
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import "YYViewController.h" 10 #import "YYappModel.h" 11 #import "YYdownLoadOperation.h" 12 13 @interface YYViewController ()<YYdownLoadOperationDelegate>
14 @property(nonatomic,strong)NSArray *apps;
15 @property(nonatomic,strong)NSOperationQueue *queue;
16 17 @end 18 19 @implementation YYViewController
20 #pragma mark- 懒加载apps
21 -(NSArray *)apps
22 {
23 if (_apps==nil) {
24 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
25 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
26 27 //字典转模型 28 NSMutableArray *array=[NSMutableArray array];
29 for (NSDictionary *dict in tempArray) {
30 YYappModel *app=[YYappModel appModelWithDict:dict];
31  [array addObject:app];
32  }
33 _apps=array;
34  }
35 return _apps;
36 }
37 38 #pragma mark-懒加载queue
39 -(NSOperationQueue *)queue
40 {
41 if (_queue==Nil) {
42 _queue=[[NSOperationQueue alloc]init];
43 //设置最大并发数为3 44 _queue.maxConcurrentOperationCount=3;
45  }
46 return _queue;
47 }
48 49 #pragma mark-数据源方法
50 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
51 {
52 return self.apps.count;
53 }
54 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
55 {
56 static NSString *ID=@"ID";
57 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
58 if (cell==nil) {
59 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
60  }
61 YYappModel *app=self.apps[indexPath.row];
62 cell.textLabel.text=app.name;
63 cell.detailTextLabel.text=app.download;
64 65 //下载图片数据
66 // NSLog(@"加载图片数据---%@", [NSThread currentThread]);
67 // NSURL *url=[NSURL URLWithString:app.icon];
68 // NSData *data=[NSData dataWithContentsOfURL:url];
69 // UIImage *imgae=[UIImage imageWithData:data];
70 // cell.imageView.image=imgae;
71 72 //创建一个OPeration对象 73 YYdownLoadOperation *operation=[[YYdownLoadOperation alloc]init];
74 operation.url=app.icon;
75 operation.indexPath=indexPath;
76 operation.delegate=self;
77 78 //把操作对象添加到队列中在去 79  [self.queue addOperation:operation];
80 81 // NSLog(@"完成显示"); 82 return cell;
83 }
84 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
85 {
86 //返回图片数据给每行对应的cell的imageview.image
87 //取出tableview中indexPath这一行对应的cell 88 UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:operation.indexPath];
89 //显示图片 90 cell.imageView.image=image;
91 // NSLog(@"cell--index--%@---%@",operation.indexPath,[NSThread currentThread]);
92 //一定要刷新表格 93  [self.tableView reloadData];
94 NSLog(@"--%@--",[NSThread currentThread]);
95 96 }
97 @end
复制代码

说明:通过打印可以发现上面的代码存在很大的问题。

问题1:需要保证一个url对应一个operation对象。

问题2:下载完需要移除。移除执行完毕的操作。

问题3:保证一个url对应一个image。

下面对主控制器中得代码进行改进:

复制代码
 1 //  2 // YYViewController.m
 3 // 01-自定义Operation
 4 //  5 // Created by apple on 14-6-26.
 6 // Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8  9 #import "YYViewController.h"  10 #import "YYappModel.h"  11 #import "YYdownLoadOperation.h"  12  13 @interface YYViewController ()<YYdownLoadOperationDelegate>
 14 @property(nonatomic,strong)NSArray *apps;
 15 @property(nonatomic,strong)NSOperationQueue *queue;
 16 @property(nonatomic,strong)NSMutableDictionary *operations;
 17 @property(nonatomic,strong)NSMutableDictionary *images;
 18  19 @end  20  21 @implementation YYViewController
 22 #pragma mark- 懒加载apps
 23 -(NSArray *)apps
 24 {
 25 if (_apps==nil) {
 26 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
 27 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
 28  29 //字典转模型  30 NSMutableArray *array=[NSMutableArray array];
 31 for (NSDictionary *dict in tempArray) {
 32 YYappModel *app=[YYappModel appModelWithDict:dict];
 33  [array addObject:app];
 34  }
 35 _apps=array;
 36  }
 37 return _apps;
 38 }
 39  40 #pragma mark-懒加载queue
 41 -(NSOperationQueue *)queue
 42 {
 43 if (_queue==Nil) {
 44 _queue=[[NSOperationQueue alloc]init];
 45 //设置最大并发数为3  46 _queue.maxConcurrentOperationCount=3;
 47  }
 48 return _queue;
 49 }
 50  51 #pragma mark-懒加载operations
 52 -(NSMutableDictionary *)operations
 53 {
 54 if (_operations==Nil) {
 55 _operations=[NSMutableDictionary dictionary];
 56  }
 57 return _operations;
 58 }
 59  60 #pragma mark-懒加载images
 61 -(NSMutableDictionary *)images
 62 {
 63 if (_images==Nil) {
 64 _images=[NSMutableDictionary dictionary];
 65  }
 66 return _images;
 67 }
 68  69 #pragma mark-数据源方法
 70 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 71 {
 72 return self.apps.count;
 73 }
 74 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 75 {
 76 static NSString *ID=@"ID";
 77 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
 78 if (cell==nil) {
 79 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
 80  }
 81 YYappModel *app=self.apps[indexPath.row];
 82 cell.textLabel.text=app.name;
 83 cell.detailTextLabel.text=app.download;
 84  85 //保证一个url对应一个image对象  86 UIImage *image=self.images[app.icon];
 87 if (image) {//缓存中有图片  88 cell.imageView.image=image;
 89 }else // 缓存中没有图片,得下载  90  {
 91 //先设置一张占位图片  92 cell.imageView.image=[UIImage imageNamed:@"57437179_42489b0"];
 93 YYdownLoadOperation *operation=self.operations[app.icon];
 94 if (operation) {//正在下载
 95 //什么都不做  96 }else //当前没有下载,那就创建操作  97  {
 98 operation=[[YYdownLoadOperation alloc]init];
 99 operation.url=app.icon;
100 operation.indexPath=indexPath;
101 operation.delegate=self;
102 [self.queue addOperation:operation];//异步下载 103 self.operations[app.icon]=operation;
104  }
105  }
106 107 108 return cell;
109 }
110 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
111 {
112 //1.移除执行完毕的操作 113  [self.operations removeObjectForKey:operation.url];
114 115 //2.将图片放到缓存中 116 self.images[operation.url]=image;
117 118 //3.刷新表格(只刷新下载的那一行) 119 120  [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
121 NSLog(@"--%d--%@--",operation.indexPath.row,[NSThread currentThread]);
122 123 }
124 @end
复制代码

打印查看:

目录
相关文章
|
16天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
1月前
|
iOS开发 UED
实现一个自定义的iOS动画效果
【4月更文挑战第9天】本文将详细介绍如何在iOS平台上实现一个自定义的动画效果。我们将通过使用Core Animation框架来实现这个动画效果,并展示如何在不同的场景中使用它。文章的目标是帮助读者理解如何使用Core Animation框架来创建自定义动画,并提供一个简单的示例代码。
18 1
|
13天前
|
前端开发 Android开发 iOS开发
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
【4月更文挑战第30天】Flutter 框架实现跨平台移动应用,通过一致的 UI 渲染(Skia 引擎)、热重载功能和响应式框架提高开发效率和用户体验。然而,Android 和 iOS 的系统差异、渲染机制及编译过程影响性能。性能对比显示,iOS 可能因硬件优化提供更流畅体验,而 Android 更具灵活性和广泛硬件支持。开发者可采用代码、资源优化和特定平台优化策略,利用性能分析工具提升应用性能。
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
|
13天前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
14天前
|
存储 Swift iOS开发
使用Swift开发一个简单的iOS应用的详细步骤。
使用Swift开发iOS应用的步骤包括:创建Xcode项目,设计界面(Storyboard或代码),定义数据模型,实现业务逻辑,连接界面和逻辑,处理数据存储(如Core Data),添加网络请求(必要时),调试与测试,根据测试结果优化改进,最后提交至App Store或其它平台发布。
32 0
|
14天前
|
安全 调度 Swift
【Swift开发专栏】Swift中的多线程与并发编程
【4月更文挑战第30天】本文探讨Swift中的多线程与并发编程,分为三个部分:基本概念、并发编程模型和最佳实践。介绍了线程、进程、并发与并行、同步与异步的区别。Swift的并发模型包括GCD、OperationQueue及新引入的结构体Task和Actor。编写高效并发代码需注意任务粒度、避免死锁、使用线程安全集合等。Swift 5.5的并发模型简化了异步编程。理解并掌握这些知识能帮助开发者编写高效、安全的并发代码。
|
14天前
|
安全 Swift iOS开发
【Swift 开发专栏】Swift 与 UIKit:构建 iOS 应用界面
【4月更文挑战第30天】本文探讨了Swift和UIKit在构建iOS应用界面的关键技术和实践方法。Swift的简洁语法、类型安全和高效编程模型,加上与UIKit的紧密集成,使开发者能便捷地创建用户界面。UIKit提供视图、控制器、布局、动画和事件处理等功能,支持灵活的界面设计。实践中,遵循设计原则,合理组织视图层次,运用布局和动画,以及实现响应式设计,能提升界面质量和用户体验。文章通过登录、列表和详情界面的实际案例展示了Swift与UIKit的结合应用。
|
14天前
|
存储 安全 Swift
【Swift 开发专栏】使用 Swift 开发一个简单的 iOS 应用
【4月更文挑战第30天】本文介绍了使用 Swift 开发简单 iOS 待办事项应用的步骤。首先,阐述了 iOS 开发的吸引力及 Swift 语言的优势。接着,详细说明了应用的需求和设计,包括添加、查看和删除待办事项的功能。开发步骤包括创建项目、界面搭建、数据存储、功能实现,并提供了相关代码示例。最后,强调了实际开发中需注意的细节和优化,旨在帮助初学者掌握 Swift 和 iOS 开发基础。
|
14天前
|
安全 Java 开发者
构建高效微服务架构:后端开发的新范式Java中的多线程并发编程实践
【4月更文挑战第29天】在数字化转型的浪潮中,微服务架构已成为软件开发的一大趋势。它通过解耦复杂系统、提升可伸缩性和促进敏捷开发来满足现代企业不断变化的业务需求。本文将深入探讨微服务的核心概念、设计原则以及如何利用最新的后端技术栈构建和部署高效的微服务架构。我们将分析微服务带来的挑战,包括服务治理、数据一致性和网络延迟问题,并讨论相应的解决方案。通过实际案例分析和最佳实践的分享,旨在为后端开发者提供一套实施微服务的全面指导。 【4月更文挑战第29天】在现代软件开发中,多线程技术是提高程序性能和响应能力的重要手段。本文通过介绍Java语言的多线程机制,探讨了如何有效地实现线程同步和通信,以及如
|
22天前
|
iOS开发 开发者 UED
利用SwiftUI构建动态列表:iOS开发的新范式
【4月更文挑战第22天】在本文中,我们将深入探讨如何使用SwiftUI来创建动态列表。SwiftUI是苹果最新推出的用户界面工具集,它允许开发者以声明式的方式描述用户界面,从而简化了代码的复杂性。我们将通过具体的代码实例,展示如何利用SwiftUI的List和ForEach视图来创建动态列表,并讨论其在实际开发中的应用。
20 2