因为Pthread很少用到,所以对于Pthread的知识没有抠那么细致,所以将Pthread和 NSThread放在了一起。
4.1 Pthread
4.1-1.0 创建线程 - pthread_create
1 /* 2 <#pthread_t *restrict#> 线程的id,指向线程标识符的指针,C 语言中类型的结尾通常 _t/Ref,而且不需要使用 * 3 <#const pthread_attr_t *restrict#> 用来设置线程的属性 (一般为 NULL) 4 <#void *(*)(void *)#> 新建立的线程执行代码的函数,线程开启后需要调用的函数或方法 (指向函数的指针) 5 <#void *restrict#> 运行函数的参数,线程的限制 (一般为 NULL) 6 */ 7 8 返回值: 9 - 若线程创建成功,则返回010 - 若线程创建失败,则返回出错编号11 12 pthread_create(13 <#pthread_t *restrict#>, // pthread_t :线程标识符.14 <#const pthread_attr_t *restrict#>,15 <#void *(*)(void *)#>,16 <#void *restrict#>17 );
4.1-1.1 __bridge 桥接
__bridge 桥接:
1、在c语言和OC之间,对数据类型进行转成换 2、通过桥接告诉c语言的函数,name就由C语言去管了
|
桥接的目的 : 就是为了告诉编译器如何管理内存,为OC添加自动内存管理操作
|
小结 :
- 在 C 语言中,没有对象的概念,对象是以结构体的方式来实现的
- 通常,在 C 语言框架中,对象类型以 _t/Ref 结尾,而且声明时不需要使用 *
- C 语言中的 void * 和 OC 中的 id 是等价的
- 在混合开发时,如果在 C 和 OC 之间传递数据,需要使用 __bridge 进行桥接,
- 桥接的添加可以借助 Xcode 的辅助功能添加
number = 1: 表示 主线程 number != 1: 表示 子线程 C语言中 void * == OC中的id C语言的数据类型,一般以 Ref / _t
|
|
4.1-1.2【代码】Pthread 1 首先导入头文件 2 3 #import <pthread.h> 4 5 代码创建: 6 7 // 创建线程,并且在线程中执行 demo 函数 8 - (void)pthreadDemo { 9 10 pthread_t threadId = NULL;11 NSString *str = @"Hello Pthread";12 13 int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));14 15 if (result == 0) {16 NSLog(@"创建线程 OK");17 } else {18 NSLog(@"创建线程失败 %d", result);19 }20 }21 22 // 后台线程调用函数23 void *demo(void *params) {24 NSString *str = (__bridge NSString *)(params);25 26 NSLog(@"%@ - %@", [NSThread currentThread], str);27 28 return NULL;29 }
4.2 NSThread
4.2-1.0 创建线程
- 第一种:通过NSThread的对象方法 (alloc / init - start)
- 第二种:通过NSThread的类方法 (detachNewThreadSelector)
- 第三种:通过NSObject的方法
|
4.2-1.1 创建线程1 - 对象方法alloc/init
一个NSThread对象就代表一条线程
1 创建方式1 : 通过NSThread的对象方法 (先创建初始化线程alloc/init , 再 start 开启线程) ——调试方便2 3 NSThread *thread = [[NSThread alloc]initWithTarget:<#(nonnull id)#>4 selector:<#(nonnull SEL)#>5 object:<#(nullable id)#> ];
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @end 5 6 @implementation ViewController 7 8 - (void)viewDidLoad { 9 [super viewDidLoad];10 // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14 [self threadDemo1];15 }16 17 #pragma mark - 对象方法alloc/init18 /*19 - 在 OC 中,任何一个方法的代码都是从上向下顺序执行的20 - 同一个方法内的代码,都是在相同线程执行的(block除外)21 */22 - (void)threadDemo1 {23 NSLog(@"before %@", [NSThread currentThread]);24 25 // 线程一启动,就会在线程thread中执行self的run方法26 NSThread *thread = [[NSThread alloc] initWithTarget:self27 selector:@selector(longOperation:)28 object:@"THREAD"];29 30 //开启线程,通过start方法,就会将我们创建出来的当前线程加入到`可调度线程池`,供CPU调度31 //[thread start];执行后,会在另外一个线程执行 longOperation: 方法32 [thread start];33 34 NSLog(@"after %@", [NSThread currentThread]);35 }36 37 - (void)longOperation:(id)obj {38 NSLog(@"%@ - %@", [NSThread currentThread], obj);39 }40 41 - (void)didReceiveMemoryWarning {42 [super didReceiveMemoryWarning];43 // Dispose of any resources that can be recreated.44 }45 46 @end
打印结果:2016-03-17 18:19:41.878 创建线程1 - 对象方法[2543:387435] before <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}2016-03-17 18:19:41.880 创建线程1 - 对象方法[2543:387711] <NSThread: 0x7ffd28d77be0>{number = 2, name = (null)} - THREAD2016-03-17 18:19:41.880 创建线程1 - 对象方法[2543:387435] after <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}
4.2-1.1.1 Target
NSThread 的实例化方法中的 target 指的是开启线程后,在线程中执行 哪一个对象 的 @selector 方法
1 NSThread *thread = [[NSThread alloc] initWithTarget:self.person2 selector:@selector(longOperation:)3 object:@"THREAD"];4 5 [thread start];
- 通过指定不同的 target 会在后台线程执行该对象的 @selector 方法
- 不要看见 target 就写 self
4.2-1.2 创建线程2 - 类方法
1 创建方式2 : 通过NSThread的类方法 (创建线程后直接自动启动线程)2 3 [NSThread detachNewThreadSelector:<#(nonnull SEL)#>4 toTarget:<#(nonnull id)#>5 withObject:<#(nullable id)#> ];
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @end 5 6 @implementation ViewController 7 8 - (void)viewDidLoad { 9 [super viewDidLoad];10 // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14 [self threadDemo2];15 }16 17 #pragma mark - 类方法18 - (void)threadDemo2 {19 NSLog(@"before %@", [NSThread currentThread]);20 21 // detachNewThreadSelector 类方法不需要启动,会自动创建线程并执行@selector方法22 // 它会自动给我们做两件事 : 1.创建线程对象 2.添加到`可调度线程池`23 // 通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度24 [NSThread detachNewThreadSelector:@selector(longOperation:)25 toTarget:self26 withObject:@"DETACH"];27 28 NSLog(@"after %@", [NSThread currentThread]);29 }30 31 - (void)longOperation:(id)obj {32 NSLog(@"%@ - %@", [NSThread currentThread], obj);33 }34 35 - (void)didReceiveMemoryWarning {36 [super didReceiveMemoryWarning];37 // Dispose of any resources that can be recreated.38 }39 40 @end
打印结果:2016-03-17 18:36:05.339 创建线程2 - 类方法[2647:404930] before <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}2016-03-17 18:36:05.340 创建线程2 - 类方法[2647:404930] after <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}2016-03-17 18:36:05.340 创建 线程2 - 类方法[2647:405061] <NSThread: 0x7fddf8e0e7a0>{number = 2, name = (null)} - DETACH
4.2-1.3 创建线程3 - 分类方法(NSObject)
1 创建方式3 : 通过NSObject的分类方法 (隐式创建并直接自动启动线程) ——推荐,开发常用2 3 // 此方法在后台线程中执行 (即是 : 在子线程中执行)4 [self performSelectorInBackground:<#(nonnull SEL) #>5 withObject:<#(nullable id) #> per];
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @end 5 6 @implementation ViewController 7 8 - (void)viewDidLoad { 9 [super viewDidLoad];10 // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14 [self threadDemo3];15 }16 17 #pragma mark - 分类方法18 - (void)threadDemo3 {19 NSLog(@"before %@", [NSThread currentThread]);20 21 // performSelectorInBackground 是NSObject的分类方法,会自动在后台线程执行@selector方法22 // 没有 thread 字眼,隐式创建并启动线程23 // 所有 NSObject 都可以使用此方法,在其他线程执行方法24 // 通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度25 // PerformSelectorInBackground 可以让方便地在后台线程执行任意NSObject对象的方法26 [self performSelectorInBackground:@selector(longOperation:)27 withObject:@"PERFORM"];28 29 NSLog(@"after %@", [NSThread currentThread]);30 }31 32 - (void)longOperation:(id)obj {33 NSLog(@"%@ - %@", [NSThread currentThread], obj);34 }35 36 - (void)didReceiveMemoryWarning {37 [super didReceiveMemoryWarning];38 // Dispose of any resources that can be recreated.39 }40 41 @end
打印结果:2016-03-17 18:41:58.696 创建线程3 - 分类方法[2711:412522] before <NSThread: 0x7ff078c02320>{number = 1, name = main}2016-03-17 18:41:58.696 创建线程3 - 分类方法[2711:412522] after <NSThread: 0x7ff078c02320>{number = 1, name = main}2016-03-17 18:41:58.697 创建线程3 - 分类方法[2711:412751] <NSThread: 0x7ff078c0e390>{number = 2, name = (null)} - PERFORM
方式2和方式3的优缺点 : |
优点:简单快捷 缺点:无法对线程进行更详细的设置
|
4.2-2.0 线程属性
1 主线程相关方法 : 2 3 + (NSThread *)mainThread; // 获得主线程 4 - (BOOL)isMainThread; // 是否为主线程 5 + (BOOL)isMainThread; // 是否为主线程 6 7 NSThread *main = [NSThread mainThread]; // + (NSThread *)mainThread; 获得主线程 8 9 [NSThread isMainThread]; // + (BOOL)isMainThread; 类方法判断,该方法是否为主线程10 11 [main isMainThread]; // - (BOOL)isMainThread; 对象方法判断,该对象是否为主线程
1 其他用法: 2 (1) currentThread - 获得当前线程 : 3 4 举例 : 5 NSThread *current = [NSThread currentThread]; //获得当前线程 6 7 (2) threadPriority - 线程的调度优先级 : 8 9 优先级,是一个浮点数,取值范围从 0~1.0 默认优先级是0.5 值越大,优先级越高10 11 优先级高只是保证 CPU 调度的可能性会高12 13 建议:在开发的时候,不要修改优先级14 多线程的目的:是将耗时的操作放在后台,不阻塞主线程和用户的交互!15 多线程开发的原则:简单16 17 //返回当前方法所在线程的优先级18 + (double)threadPriority;19 举例:[NSThread threadPriority];20 21 //设置线程的优先级22 + (BOOL)setThreadPriority:(double)p;23 举例:self.thread1.threadPriority = 1.0;24 25 - (double)threadPriority;//返回当前方法所在线程的优先级26 - (BOOL)setThreadPriority:(double)p;//设置线程的优先级27 28 (3) name - 线程的名字 : 在大的商业项目中,通常需要在程序崩溃时,获取程序准确执行所在的线程29 30 - (void)setName:(NSString *)n; //set 方法31 - (NSString *)name; //get 方法32 33 举例:34 thread.name = @"线程A";35 36 (4) stackSize - 栈区大小37 38 - 默认情况下,无论是主线程还是子线程,栈区大小都是 512K39 - 栈区大小虽然可以设置,但是我们一般都使用系统默认的大小就行了40 41 举例:42 [NSThread currentThread].stackSize = 1024 * 1024;
代码示例:
1 // 线程属性 2 - (void)threadProperty { 3 NSThread *t1 = [[NSThread alloc] initWithTarget:self 4 selector:@selector(demo) 5 object:nil]; 6 7 // 1. 线程名称 8 t1.name = @"Thread AAA"; 9 10 // 2. 优先级11 t1.threadPriority = 0;12 13 [t1 start];14 15 NSThread *t2 = [[NSThread alloc] initWithTarget:self16 selector:@selector(demo)17 object:nil];18 // 1. 线程名称19 t2.name = @"Thread BBB";20 // 2. 优先级21 t2.threadPriority = 1;22 23 [t2 start];24 }25 26 - (void)demo {27 for (int i = 0; i < 10; ++i) {28 // 堆栈大小29 NSLog(@"%@ 堆栈大小:%tuK", [NSThread currentThread],[NSThread currentThread].stackSize / 1024);30 }31 32 // 模拟崩溃33 // 判断是否是主线程34 if (![NSThread currentThread].isMainThread) {35 NSMutableArray *a = [NSMutableArray array];36 [a addObject:nil];37 }38 }
4.2-3.0 线程状态/线程生命周期
- 新建状态
- 就绪状态/启动状态 : 线程在可调度线程池中
- 运行状态
- 阻塞状态/暂停线程 : 线程不在可调度线程池中,但是仍然存在内存中,只是不可用
- 死亡状态 : 线程不在内存中
|
(1)新建状态 : 实例化线程对象
说明:创建线程有多种方式,这里不做过多的介绍
(2)就绪状态 / 启动线程: ( 进入就绪状态 ->运行状态。当线程任务执行完毕,自动进入死亡状态 )
线程开启 : 线程进入可调度线程池
- 向线程对象发送 start 消息,线程对象被加入可调度线程池等待 CPU 调度
- detachNewThreadSelector 方法和 performSelectorInBackground 方**直接实例化一个线程对象并加入可调度线程池
|
(3)运行状态:
- CPU 负责调度可调度线程池中线程的执行
- 线程执行完成之前(死亡之前),状态可能会在就绪和运行之间来回切换
- 就绪和运行之间的状态变化由 CPU 负责,程序员不能干预
|
|
- 当 CPU 调度当前线程 , 进入运行状态
- 当 CPU 调度其他线程 , 进入就绪状态
|
(4)阻塞状态 / 暂停线程 : 当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
1 方法执行过程,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态 2 3 sleepForTimeInterval: //休眠指定时长 (从现在起睡多少秒) 4 sleepUntilDate: //休眠到指定日期 (从现在起睡到指定的日期) 5 @synchronized(self) { } //互斥锁 6 7 (1)阻塞2秒 8 [NSThread sleepForTimeInterval:2]; // 阻塞状态 9 10 (2)以当前时间为基准阻塞4秒11 NSDate *date = [NSDate dateWithTimeIntervalSinceNow:4.0]; //从现在开始多少秒12 [NSThread sleepUntilDate:date]; //睡眠多少秒
(5)死亡状态 (一旦线程停止或死亡了,就不能再次开启任务 , 后续的所有代码都不会被执行 )
(1) 正常死亡 (2) 非正常死亡
- (**) 当满足某个条件后,在线程内部自己中止执行
- (被逼着死亡) 当满足某个条件后,在主线程给其它线程打个死亡标记(下圣旨),让子线程自行了断.
注意:在终止线程之前,应该注意释放之前分配的对象!
|
生命周期示意图:
images/loading.gif' data-original="http://images2015.cnblogs.com/blog/731328/201603/731328-20160325103511308-1180733546.png" />
代码示例:
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @end 5 6 @implementation ViewController 7 8 - (void)viewDidLoad { 9 [super viewDidLoad];10 // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14 15 // 1.在主线程中创建一个子线程(实例化线程对象) ---> 新建状态16 NSThread *Th =17 [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];18 19 // 2.将 Th 线程加入到可调度线程池,等待CPU调度--->就绪状态20 [Th start];21 22 // 3.让主线程阻塞,让当前线程(主线程)休眠23 [NSThread sleepForTimeInterval:1.0];24 25 // 4.在主线程给 Th 线程打死亡标签26 [Th cancel]; //只是打了个标签,并没有执行,需要在子线程中27 }28 29 // Th 线程---> 运行状态30 - (void)run {31 32 NSThread *huThread = [NSThread currentThread];33 34 CGMutablePathRef path = CGPathCreateMutable();35 36 for (int i = 0; i < 30; i++) {37 if ([huThread isCancelled]) {38 NSLog(@"good bye1");39 return; // --->非正常死亡(被逼着死亡)40 }41 42 if (i == 5) {43 [NSThread sleepForTimeInterval:3.0]; //--->huThread阻塞状态3秒44 // [NSThread sleepUntilDate:[NSDate distantFuture]]; // 睡到遥远的未来45 // [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; //线程睡到从现在开始后的2秒为止46 }47 48 if ([huThread isCancelled]) {49 NSLog(@"good bye2");50 return;51 }52 53 if (i == 20) {54 //清空资源55 CGPathRelease(path);56 57 //在调用下面方法之前,必须清空资源 非正常死亡--**(退出线程)58 [NSThread exit];59 }60 61 if ([huThread isCancelled]) {62 NSLog(@"good bye3");63 return;64 }65 NSLog(@"%d", i);66 }67 } //--->huThread死亡状态 (正常死亡状态)68 69 - (void)didReceiveMemoryWarning {70 [super didReceiveMemoryWarning];71 // Dispose of any resources that can be recreated.72 }73 74 @end
4.2-4.1 多线程安全隐患 - 资源共享/抢夺
(1) 起因 : 资源共享概念 : 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 主要是因为多条线程,对`同一资源同时操作`,导致的问题
|
(2) 举例 : 比如多个线程访问同一个对象、同一个变量、同一个文件 |
(3) 结果 : 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 (资源强夺) |
(4) 解决方案 : 互斥锁 / 同步锁 |
代码示例 : (卖票案例)
多线程开发的复杂度相对较高,在开发时可以按照以下套路编写代码:
|
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @property(nonatomic, strong) NSThread *thread01; // 售票员01 5 @property(nonatomic, strong) NSThread *thread02; // 售票员02 6 @property(nonatomic, strong) NSThread *thread03; // 售票员03 7 8 @property(nonatomic, assign) NSInteger ticketCount; //票的总数 9 @end10 11 @implementation ViewController12 13 - (void)viewDidLoad {14 [super viewDidLoad];15 // Do any additional setup after loading the view, typically from a nib.16 17 self.ticketCount = 10;18 19 //创建线程20 self.thread01 = [[NSThread alloc] initWithTarget:self21 selector:@selector(saleTicket)22 object:nil];23 self.thread01.name = @"售票员01";24 25 self.thread02 = [[NSThread alloc] initWithTarget:self26 selector:@selector(saleTicket)27 object:nil];28 self.thread02.name = @"售票员02";29 30 self.thread03 = [[NSThread alloc] initWithTarget:self31 selector:@selector(saleTicket)32 object:nil];33 self.thread03.name = @"售票员03";34 }35 36 // 开启线程37 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {38 [self.thread01 start];39 [self.thread02 start];40 [self.thread03 start];41 }42 43 // 卖票44 - (void)saleTicket {45 46 while (1) {47 48 @synchronized(self) { //互斥锁(控制器做锁对象)49 // 先取出总数50 NSInteger count = self.ticketCount;51 52 // 判断还有没有余票53 if (count > 0) {54 self.ticketCount = count - 1;55 NSLog(@"%@卖了一张票,还剩下%zd张", [NSThread currentThread].name,56 self.ticketCount);57 } else {58 NSLog(@"票已经卖完了");59 break;60 }61 }62 }63 }64 65 - (void)didReceiveMemoryWarning {66 [super didReceiveMemoryWarning];67 // Dispose of any resources that can be recreated.68 }69 70 @end
打印结果:2016-03-17 19:37:27.429 线程安全[3386:472835] 售票员02卖了一张票,还剩下9张2016-03-17 19:37:27.430 线程安全[3386:472836] 售票员03卖了一张票,还剩下8张2016-03-17 19:37:27.430 线程安全[3386:472834] 售票员01卖了一张票,还剩下7张2016-03-17 19:37:27.430 线程安全[3386:472835] 售票员02卖了一张票,还剩下6张2016-03-17 19:37:27.430 线程安全[3386:472836] 售票员03卖了一张票,还剩下5张2016-03-17 19:37:27.430 线程安全[3386:472834] 售票员01卖了一张票,还剩下4张2016-03-17 19:37:27.431 线程安全[3386:472835] 售票员02卖了一张票,还剩下3张2016-03-17 19:37:27.431 线程安全[3386:472836] 售票员03卖了一张票,还剩下2张2016-03-17 19:37:27.431 线程安全[3386:472834] 售票员01卖了一张票,还剩下1张2016-03-17 19:37:27.431 线程安全[3386:472835] 售票员02卖了一张票,还剩下0张2016-03-17 19:37:27.432 线程安全[3386:472836] 票已经卖完了2016-03-17 19:37:27.434 线程安全[3386:472834] 票已经卖完了2016-03-17 19:37:27.434 线程安全[3386:472835] 票已经卖完了
4.2-4.2 安全隐患解决 – 互斥锁 / 同步锁
互斥锁使用技术 : 线程同步
概念:多条线程按顺序地执行任务 |
引申 : 互斥锁,就是使用了线程同步技术 |
互斥锁使用格式 :
1 //锁对象为能够加锁的任意 NSObject 对象2 //锁对象一定要保证所有的线程都能够访问3 //如果代码中只有一个地方需要加锁,大多都使用self,这样可以避免单独再创建一个锁对象4 5 @synchronized(锁对象) {6 //需要锁定的代码7 }
注意:
- 锁定1份代码只用1把锁,用多把锁是无效的
- 保证锁内的代码,同一时间,只有一条线程能够执行!
- 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!
- 速记 : [[NSUserDefaults standardUserDefaults] synchronize];
|
互斥锁的使用前提:
互斥锁的优缺点 :
优点:能有效防止因多线程抢夺资源造成的数据安全问题 , 能保证数据的准确性 |
缺点:需要消耗大量的CPU资源 , 可能会导致执行速度变慢 |
4.2-4.3 原子属性/非原子属性原子和非原子属性:
(默认) atomic | 原子属性 | 为setter方法加锁 | 线程安全,需要消耗大量的资源 |
(推荐) nonatomic | 非原子属性 | 不会为setter方法加锁 | 非线程安全,适合内存小的移动设备 |
atomic加锁原理:
1 @property (assign, atomic) int age;2 - (void)setAge:(int)age3 {4 @synchronized(self) {5 _age = age;6 }7 }
iOS开发的建议:
- 所有属性都声明为nonatomic
- 尽量避免多线程抢夺同一块资源
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
|
4.2-5.0 线程间通讯和常用方法线程间通信:
概念 : 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 |
线程间通信的体现 :
- 1个线程传递数据给另1个线程
- 在1个线程中执行完特定任务后,转到另1个线程继续执行任务
|
线程间通信常用方法 :
1 //在主线程执行操作 (从子线程回到主线程)(推荐)2 - (void)performSelectorOnMainThread:(SEL)aSelector3 withObject:(id)arg4 waitUntilDone:(BOOL)wait;5 6 - (void)performSelector:(SEL)aSelector7 onThread:(NSThread *)thr8 withObject:(id)arg9 waitUntilDone:(BOOL)wait;
另外一种线程之间的通信方式:NSPort(端口)
包括的子类: (1)NSMessagePort; (2)NSMachPort;
|
4.2-5.1 图片下载示例
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @property(weak, nonatomic) IBOutlet UIImageView *imageView; 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad {10 [super viewDidLoad];11 // Do any additional setup after loading the view, typically from a nib.12 }13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {15 // 开启子线程(在子线程中调用download方法下载图片)16 [self performSelectorInBackground:@selector(download) withObject:nil];17 }18 19 #pragma mark - 图片下载20 - (void)download {21 // 1.图片的网络路径22 NSURL *url =23 [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];24 25 // 2.下载图片并把图片转换为二进制的数据(耗时操作)26 NSData *data = [NSData dataWithContentsOfURL:url];27 28 // 3.生成图片(把数据转换成图片)29 UIImage *image = [UIImage imageWithData:data];30 31 // 4.回到主线程中设置图片32 // 方法1:33 [self.imageView34 performSelector:@selector(setImage:)35 onThread:[NSThread mainThread]36 withObject:image37 waitUntilDone:NO]; //是否等到@selector的方法完成后再往下执行,NO表示否38 39 //方法2:40 [self.imageView performSelectorOnMainThread:@selector(setImage:)41 withObject:image42 waitUntilDone:NO];43 // 方法3:代码一44 [self performSelectorOnMainThread:@selector(showImage:)45 withObject:image46 waitUntilDone:NO];47 }48 49 // 方法3:代码二50 - (void)showImage:(UIImage *)image {51 self.imageView.image = image; //设置显示图片52 }53 54 #pragma mark - 测试图片下载时间 方法1:NSDate55 - (void)download1 {56 // 1.图片的网络路径57 NSURL *url =58 [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];59 60 NSDate *begin = [NSDate date]; //开始时间61 62 // 2.根据图片的网络路径去下载图片数据63 NSData *data = [NSData dataWithContentsOfURL:url];64 65 NSDate *end = [NSDate date]; //结束时间66 67 NSLog(@"%f", [end timeIntervalSinceDate:begin]); // 时间间隔 = 开始-结束68 69 // 3.显示图片70 self.imageView.image = [UIImage imageWithData:data];71 }72 73 #pragma mark - 测试图片下载时间 方法2:CFTimeInterval74 - (void)download2 {75 // 1.图片的网络路径76 NSURL *url =77 [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];78 79 CFTimeInterval begin = CFAbsoluteTimeGetCurrent(); // 开始时间80 81 // 2.根据图片的网络路径去下载图片数据82 NSData *data = [NSData dataWithContentsOfURL:url];83 84 CFTimeInterval end = CFAbsoluteTimeGetCurrent(); //结束时间85 86 NSLog(@"%f", end - begin); // 时间间隔 = 开始-结束87 88 // 3.显示图片89 self.imageView.image = [UIImage imageWithData:data];90 }91 92 - (void)didReceiveMemoryWarning {93 [super didReceiveMemoryWarning];94 // Dispose of any resources that can be recreated.95 }96 97 @end
如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^ 作者:蓝田(Loto)出处:http://www.cnblogs.com/shorfng/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
如有疑问,请发送邮件至 shorfng@126.com 联系我。
原标题:4.1/4.2 多线程进阶篇上(Pthread NSThread)
关键词:线程