A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 602516169 中级黑马   /  2016-8-27 10:30  /  700 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

简介
iOS有三种多线程编程的技术,分别是:
(一)NSThread
(二)Cocoa NSOperation
(三)GCD(全称:Grand Central Dispatch)

这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。

三种方式的优缺点介绍:
1)NSThread:
优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

NSThread实现的技术有下面三种:
~MA59HJ2_9MN7%]4Y%}FDGW
一般使用cocoa thread 技术。

Cocoa NSOperation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。

GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到7了,所以不用担心该技术不能使用。

介绍完这三种多线程编程方式,本文将依次介绍这三种技术的使用。

(一)NSThread的使用
NSThread 有两种直接创建方式:


1
2
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
第一个是实例方法,第二个是类方法


1
2
3
4
5
6
1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];   

2、NSThread* myThread = [[NSThread alloc] initWithTarget:self   
                                        selector:@selector(doSomething:)   
                                        object:nil];   
[myThread start];
参数的意义:
selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。
target  :selector消息发送的对象
argument:传输给target的唯一参数,也可以是nil

第一种方式会直接创建线程并且开始运行线程,第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息

不显式创建线程的方法:
用NSObject的类方法  performSelectorInBackground:withObject: 创建一个线程:


1
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
下载图片的例子:
新建singeView app
新建项目,并在xib文件上放置一个imageView控件。按住control键拖到viewController.h文件中创建imageView IBOutlet ViewController.m中实现:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//   
//  ViewController.m   
//  NSThreadDemo   
//   
//  Created by rongfzh on 12-9-23.   
//  Copyright (c) 2012年 rongfzh. All rights reserved.   
//   

#import "ViewController.h"   
#define kURL @"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"   
@interface ViewController ()   

@end   

@implementation ViewController   

-(void)downloadImage:(NSString *) url{   
    NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];   
    UIImage *image = [[UIImage alloc]initWithData:data];   
    if(image == nil){   

    }else{   
        [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];   
    }   
}   

-(void)updateUI:(UIImage*) image{   
    self.imageView.image = image;   
}   

- (void)viewDidLoad   
{   
    [super viewDidLoad];   

//    [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];   
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];   
    [thread start];   
}   

- (void)didReceiveMemoryWarning   
{   
    [super didReceiveMemoryWarning];   
    // Dispose of any resources that can be recreated.   
}   

@end
线程间通讯
线程下载完图片后怎么通知主线程更新界面呢?


1
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:


1
performSelector:onThread:withObject:waitUntilDone:
运行下载图片:
VFE8W}]XP~E6K0RPJK10EIU

线程同步
我们演示一个经典的卖票的例子来讲NSThread的线程同步:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <UIKit/UIKit.h>   

@class ViewController;   

@interface AppDelegate : UIResponder <UIApplicationDelegate>   
{   
    int tickets;   
    int count;   
    NSThread* ticketsThreadone;   
    NSThread* ticketsThreadtwo;   
    NSCondition* ticketsCondition;   
    NSLock *theLock;   
}   
@property (strong, nonatomic) UIWindow *window;   

@property (strong, nonatomic) ViewController *viewController;   

@end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions   
{   

    tickets = 100;   
    count = 0;   
    theLock = [[NSLock alloc] init];   
    // 锁对象   
    ticketsCondition = [[NSCondition alloc] init];   
    ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
    [ticketsThreadone setName:@"Thread-1"];   
    [ticketsThreadone start];   

    ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
    [ticketsThreadtwo setName:@"Thread-2"];   
    [ticketsThreadtwo start];   

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];   
    // Override point for customization after application launch.   
    self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];   
    self.window.rootViewController = self.viewController;   
    [self.window makeKeyAndVisible];   
    return YES;   
}   

- (void)run{   
    while (TRUE) {   
        // 上锁   
//        [ticketsCondition lock];   
        [theLock lock];   
        if(tickets >= 0){   
            [NSThread sleepForTimeInterval:0.09];   
            count = 100 - tickets;   
            NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);   
            tickets--;   
        }else{   
            break;   
        }   
        [theLock unlock];   
//        [ticketsCondition unlock];   
    }   
}
如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。

上面例子我使用了两种锁,一种NSCondition ,一种是:NSLock。 NSCondition我已经注释了。

线程的顺序执行
他们都可以通过[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。

比如:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#import "AppDelegate.h"   

#import "ViewController.h"   

@implementation AppDelegate   

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions   
{   

    tickets = 100;   
    count = 0;   
    theLock = [[NSLock alloc] init];   
    // 锁对象   
    ticketsCondition = [[NSCondition alloc] init];   
    ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
    [ticketsThreadone setName:@"Thread-1"];   
    [ticketsThreadone start];   

    ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
    [ticketsThreadtwo setName:@"Thread-2"];   
    [ticketsThreadtwo start];   

    NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];   
    [ticketsThreadthree setName:@"Thread-3"];   
    [ticketsThreadthree start];      
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];   
    // Override point for customization after application launch.   
    self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];   
    self.window.rootViewController = self.viewController;   
    [self.window makeKeyAndVisible];   
    return YES;   
}   
http://en.wikipedia.org/wiki/Grand_Central_Dispatch

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马