iOS多线程编程之NSThread的使用(转载)
1、简介:
1.1 iOS有三种多线程编程的技术,分别是:
1、NSThread
2、Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用)
3、GCD 全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用)
这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
1.2 三种方式的优缺点介绍:
NSThread:
优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
NSThread实现的技术有下面三种:
Cocoa operation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到6了,所以不用担心该技术不能使用。
介绍完这三种多线程编程方式,我们这篇先介绍NSThread的使用。
2、NSThread的使用
2.1 NSThread 有两种直接创建方式:
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
第一个是实例方法,第二个是类方法
1 | 1、[ NSThread detachNewThreadSelector: @selector (doSomething:) toTarget: self withObject: nil ]; |
1 2 3 4 | 2、 NSThread * myThread = [[ NSThread alloc] initWithTarget: self selector: @selector (doSomething:) object: nil ]; [myThread start]; |
2.2参数的意义:
selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。
target :selector消息发送的对象
argument:传输给target的唯一参数,也可以是nil
第一种方式会直接创建线程并且开始运行线程。
第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息。
2.3 PS:不显式创建线程的方法:
用NSObject的类方法 performSelectorInBackground:withObject: 创建一个线程:
1 | [Obj performSelectorInBackground: @selector (doSomething) withObject: nil ]; |
2.4 下载图片的例子:
2.4.1 新建singeView app
新建项目,并在xib文件上放置一个imageView控件。
按住control键拖到viewController.h文件中创建imageView IBOutlet
ViewController.m中实现: // // 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 |
2.4.2线程间通讯
线程下载完图片后怎么通知主线程更新界面呢?
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:
用:performSelector:onThread:withObject:waitUntilDone:
运行下载图片:
图片下载下来了。
2.3 线程同步
我们演示一个经典的卖票的例子来讲NSThread的线程同步:
.h
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 | #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 - ( 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 | #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 ; } -( void )run3{ while ( YES ) { [ticketsCondition lock]; [ NSThread sleepForTimeInterval:3]; [ticketsCondition signal]; [ticketsCondition unlock]; } } - ( void )run{ while (TRUE) { // 上锁 [ticketsCondition lock]; [ticketsCondition wait]; [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]; } } |
wait是等待,我加了一个 线程3 去唤醒其他两个线程锁中的wait
其他同步
我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
1 2 3 4 5 6 7 | - ( void )doSomeThing:( id )anObj { @synchronized (anObj) { // Everything between the braces is protected by the @synchronized directive. } } |
还有其他的一些锁对象,比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等,可以自己看官方文档学习
转载:http://blog.csdn.net/totogo2010/article/details/8010231
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)