iPhone开发进阶(11)--- 多线程的使用与注意事项
2015-10-30 15:48
651 查看
iPhone开发进阶(11)--- 多线程的使用与注意事项
这一回,主要介绍一下iPhone SDK中多线程的使用方法以及注意事项。虽然现在大部分PC应用程序都支持多线程/多任务的开发方式,但是在iPhone上,Apple并不推荐使用多线程的编程方式。但是多线程编程毕竟是发展的趋势,而且据说即将推出的iPhone OS4将全面支持多线程的处理方式。所以说掌握多线程的编程方式,在某些场合一定能挖掘出iPhone的更大潜力。
先从一个例程入手,具体的代码参考了这里。还有例程可以下载。
多线程程序的控制模型可以参考这里,一般情况下都是使用 管理者/工人模型, 这里,我们使用iPhone
SDK中的 NSThread 来实现它。
首先创建一个新的 View-based application 工程,名字为 "TutorialProject" 。界面如下图所示,使用UILabel实现两部分的Part(Thread Part和Test Part),Thread Part中包含一个UIProgressView和一个UIButton;而Test Part包含一个值和一个UISlider。
接下来,在 TutorialProjectViewController.h 文件中创建各个UI控件的 IBOutlets.
同时,也需要创建outlets变量的property.
接下来定义按钮按下时的动作函数,以及slider的变化函数。
然后在 TutorialProjectViewController.m 文件中synthesize outlets,并在文件为实现dealloc释放资源。
现在开始线程部分的代码,首先当 thread button 被按下的时候,创建新的线程.
该按钮被按下后,隐藏按钮以禁止多次创建线程。然后初始化显示值和进度条,最后创建新的线程,线程的函数为 startTheBackgroundJob.
具体的 startTheBackgroundJob 函数定义如下.
在第1行,创建了一个 NSAutoreleasePool 对象,用来管理线程中自动释放的对象资源。这里 NSAutoreleasePool 在线程退出的时候释放。这符合 Cocoa GUI 应用程序的一般规则。
最后一行,阻塞调用(waitUntilDone状态是ON)函数 makeMyProgressBarMoving。
这里计算用于显示的进度条的值,利用 NSTimer ,每0.5秒自增0.01,当值等于1的时候,进度条为100%,退出函数并显示刚才被隐藏的按钮。
最后,添加 UISlider 的实现函数,用来更改主线程中 Test Part 中的 label 值。
编译执行,按下线程开始按钮,你将看到进度条的计算是在后台运行。
iPhone设备上的应用程序开发也是属于嵌入式设备的开发,同样需要注意嵌入式设备开发时的几点问题,比如资源上限,处理器速度等。
iPhone 中的线程应用并不是无节制的,官方给出的资料显示iPhone
OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB。并且该值不能通过编译器开关或线程API函数来更改。
你可以用下面的例子测试你的设备,这里使用POSIX Thread(pthread),设备环境是 iPhone 3GS(16GB)、SDK是3.1.3。
结果如下:
模拟器
设备
由此可见,当你测试多线程的程序时,模拟器和实际设备的堆栈大小是不一样的。如果有大量递归函数调用可要注意了。
如果你什么都不考虑,在线程函数内调用 autorelease 、那么会出现下面的错误:
一般,在线程中使用内存的模式是,线程最初
而在线程结束的时候 [pool drain] 或 [pool release]。1
多线程编程中普遍遵循一个原则,就是一切与UI相关的操作都有主线程做,子线程只负责事务,数据方面的处理。那么如果想在子线程中更新UI时怎么做呢?如果是在windows下,你会 PostMessage 一个描画更新的消息,在iPhone中,需要使用performSelectorOnMainThread 委托主线程处理。
比如,如果在子线程中想让 UIImageView 的 image 更新,如果直接在线程中
这么做,什么也不会出现的。需要将该处理委托给主线程来做,像下面:
就OK了!
到此为止,《iPhone开发进阶》系列就告一段落了,接下来将针对不同的开发领域,总结一些小技巧与应用技术,希望您能继续关注。
1. drain
与 release 的区别前提是你的系统中是否有GC,如果有,-drain 需要送一个消息给GC (objc_collect_if_needed),而如果没有GC,drain = release
这一回,主要介绍一下iPhone SDK中多线程的使用方法以及注意事项。虽然现在大部分PC应用程序都支持多线程/多任务的开发方式,但是在iPhone上,Apple并不推荐使用多线程的编程方式。但是多线程编程毕竟是发展的趋势,而且据说即将推出的iPhone OS4将全面支持多线程的处理方式。所以说掌握多线程的编程方式,在某些场合一定能挖掘出iPhone的更大潜力。
从例子入手
先从一个例程入手,具体的代码参考了这里。还有例程可以下载。多线程程序的控制模型可以参考这里,一般情况下都是使用 管理者/工人模型, 这里,我们使用iPhone
SDK中的 NSThread 来实现它。
首先创建一个新的 View-based application 工程,名字为 "TutorialProject" 。界面如下图所示,使用UILabel实现两部分的Part(Thread Part和Test Part),Thread Part中包含一个UIProgressView和一个UIButton;而Test Part包含一个值和一个UISlider。
接下来,在 TutorialProjectViewController.h 文件中创建各个UI控件的 IBOutlets.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @interface TutorialProjectViewController : UIViewController { // ------ Tutorial code starts here ------ // Thread part IBOutlet UILabel *threadValueLabel; IBOutlet UIProgressView *threadProgressView; IBOutlet UIButton *threadStartButton; // Test part IBOutlet UILabel *testValueLabel; // ------ Tutorial code ends here ------ } |
1 2 3 4 | @property (nonatomic, retain) IBOutlet UILabel *threadValueLabel; @property (nonatomic, retain) IBOutlet UIProgressView *threadProgressView; @property (nonatomic, retain) IBOutlet UIProgressView *threadStartButton; @property (nonatomic, retain) IBOutlet UILabel *testValueLabel; |
1 2 | - (IBAction) startThreadButtonPressed:(UIButton *)sender; - (IBAction) testValueSliderChanged:(UISlider *)sender; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 | @synthesize threadValueLabel, threadProgressView, testValueLabel, threadStartButton; ... - (void)dealloc { // ------ Tutorial code starts here ------ [threadValueLabel release]; [threadProgressView release]; [threadStartButton release]; [testValueLabel release]; // ------ Tutorial code ends here ------ [super dealloc]; } |
1 2 3 45 | - (IBAction) startThreadButtonPressed:(UIButton *)sender { threadStartButton.hidden = YES; threadValueLabel.text = @"0"; threadProgressView.progress = 0.0; [NSThread detachNewThreadSelector:@selector(startTheBackgroundJob) toTarget:self withObject:nil]; } |
具体的 startTheBackgroundJob 函数定义如下.
1 2 3 45 | - (void)startTheBackgroundJob { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 线程开始后先暂停3秒(这里只是演示暂停的方法,你不是必须这么做的) [NSThread sleepForTimeInterval:3]; [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; [pool release]; } |
最后一行,阻塞调用(waitUntilDone状态是ON)函数 makeMyProgressBarMoving。
1 2 3 45 | - (void)makeMyProgressBarMoving { float actual = [threadProgressView progress]; threadValueLabel.text = [NSString stringWithFormat:@"%.2f", actual]; if (actual < 1) { threadProgressView.progress = actual + 0.01; [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(makeMyProgressBarMoving) userInfo:nil repeats:NO]; } else threadStartButton.hidden = NO; } |
最后,添加 UISlider 的实现函数,用来更改主线程中 Test Part 中的 label 值。
1 2 3 45 | - (IBAction) testValueSliderChanged:(UISlider *)sender { testValueLabel.text = [NSString stringWithFormat:@"%.2f", sender.value]; } |
使用线程的注意事项
线程的堆栈大小
iPhone设备上的应用程序开发也是属于嵌入式设备的开发,同样需要注意嵌入式设备开发时的几点问题,比如资源上限,处理器速度等。iPhone 中的线程应用并不是无节制的,官方给出的资料显示iPhone
OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB。并且该值不能通过编译器开关或线程API函数来更改。
你可以用下面的例子测试你的设备,这里使用POSIX Thread(pthread),设备环境是 iPhone 3GS(16GB)、SDK是3.1.3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 | #include "pthread.h" void *threadFunc(void *arg) { void* stack_base = pthread_get_stackaddr_np(pthread_self()); size_t stack_size = pthread_get_stacksize_np(pthread_self()); NSLog(@"Thread: base:%p / size:%u", stack_base, stack_size); return NULL; } - (void)applicationDidFinishLaunching:(UIApplication *)application { void* stack_base = pthread_get_stackaddr_np(pthread_self()); size_t stack_size = pthread_get_stacksize_np(pthread_self()); struct rlimit limit; getrlimit(RLIMIT_STACK, &limit); NSLog(@"Main thread: base:%p / size:%u", stack_base, stack_size); NSLog(@" rlimit-> soft:%llu / hard:%llu", limit.rlim_cur, limit.rlim_max); pthread_t thread; pthread_create(&thread, NULL, threadFunc, NULL); // Override point for customization after app launch [window addSubview:viewController.view]; [window makeKeyAndVisible]; } |
模拟器
Main thread: base:0xc0000000 / size:524288 rlimit-> soft:8388608 / hard:67104768 Thread: base:0xb014b000 / size:524288
设备
Main thread: base:0x30000000 / size:524288 rlimit-> soft:1044480 / hard:1044480 Thread: base:0xf1000 / size:524288
由此可见,当你测试多线程的程序时,模拟器和实际设备的堆栈大小是不一样的。如果有大量递归函数调用可要注意了。
Autorelease
如果你什么都不考虑,在线程函数内调用 autorelease 、那么会出现下面的错误:NSAutoReleaseNoPool(): Object 0x********* of class NSConreteData autoreleased with no pool in place ….
一般,在线程中使用内存的模式是,线程最初
1 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; |
子线程中描画窗口
多线程编程中普遍遵循一个原则,就是一切与UI相关的操作都有主线程做,子线程只负责事务,数据方面的处理。那么如果想在子线程中更新UI时怎么做呢?如果是在windows下,你会 PostMessage 一个描画更新的消息,在iPhone中,需要使用performSelectorOnMainThread 委托主线程处理。比如,如果在子线程中想让 UIImageView 的 image 更新,如果直接在线程中
1 | imageView.image = [UIImage imageNamed:@"Hoge.png"]; |
1 | [delegate performSelectorOnMainThread:@selector(theProcess:) withObject:nil waitUntilDone:YES]; |
到此为止,《iPhone开发进阶》系列就告一段落了,接下来将针对不同的开发领域,总结一些小技巧与应用技术,希望您能继续关注。
1. drain
与 release 的区别前提是你的系统中是否有GC,如果有,-drain 需要送一个消息给GC (objc_collect_if_needed),而如果没有GC,drain = release
相关文章推荐
- 双机高可用、负载均衡、MySQL(读写分离、主从自动切换)架构设计
- FastDFS + Nginx 反向代理缓存 安装与配置
- First Bad Version
- 不可思议的CSS border属性----纯CSS制作三角形
- noip2013 火柴排队 (离散化+归并排序求逆序对数)
- Linux:ssh_config快速访问服务器
- Linux:ssh_config快速访问服务器
- 后台更新
- Material-Movies
- cocos2d-x 与lua 通讯
- ThinkPHP单字母函数整理 A函数 B函数 C函数 D函数 F函数 L函数 R函数 S函数 U函数
- 用于删除学生的信息
- 500 Internal Server Error错误问题解决办法
- 闰平年简介及计算过程描述
- 视音频技术干货专栏
- udp打洞( NAT traversal )的方法介绍
- 芒果iOS开发之选择排序
- 【FastDev4Android框架开发】AndroidAnnnotations注入框架使用之事件绑定Event Binding(十一)
- noip2013 转圈游戏 (快速幂模)
- android 音乐波形图