在多线程中进行UI操作

转载自   http://blog.csdn.net/developer_zhang/article/details/8910919

iOS 上不建议在非主线程进行UI操作,在非主线程进行UI操作有很大几率会导致程序崩溃,或者出现预期之外的效果。

我开始不知道这一点,在子线程中进行了弹窗操作,结果程序就出问题了!

报的错误是(EXC_BAD_ACCESS(code=2,address=0xcc),0x1a0ad32: movl 204(%ecx), %edx ),我以为是空指针导致的内存泄露,用了很多方法,但这问题感觉很顽固,困扰了我很多天。

后来有位大牛指点了我,问我是不是在子线程进行这个弹窗操作。。。直到此时我才明白问题出在哪里,问题顺利解决。有时候出现bug却不知道是哪引起的,这时是最纠结的,等明确了问题所在,问题就不是问题了。好了,言归正传。

那么在子线程中的UI操作如何处理呢?有两种方法:

一:在子线程,你需要进行的UI操作前添加dispatch_async函数,即可将代码块中的工作转回到主线程

 dispatch_async(dispatch_get_main_queue(), ^{
            //更新UI操作
            //.....
        });

dispatch_async函数是异步操作,对于比较耗时的操作也可以这样处理:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // 处理耗时操作的代码块...  

    //通知主线程刷新
    dispatch_async(dispatch_get_main_queue(), ^{
        //回调或者说是通知主线程刷新,
    });  

});

dispatch_async开启一个异步操作,第一个参数是指定一个gcd队列,第二个参数是分配一个处理事物的程序块到该队列。

dispatch_get_global_queue(0, 0),指用了全局队列。

一般来说系统本身会有3个队列。

global_queue,current_queue,以及main_queue.

获取一个全局队列是接受两个参数,第一个是我分配的事物处理程序块队列优先级。分高低和默认,0为默认2为高,-2为低

二:performSelectorOnMainThread

performSelectorOnMainThread是指在主线程上执行某个方法,比如数据下载后,更新UI界面等操作

例如:在子线程中使用[self performSelectorOnMainThread:@selector(endThread) withObject:nil waitUntilDone:NO];

-(void)setupThread:(NSArray*)userInfor{  

   [NSThread detachNewThreadSelector:@selector(threadFunc:) toTarget:self withObject:(id)userInfor];  

}  

- (void)threadFunc:(id)userInfor{  

   NSAutoreleasePool*pool = [[NSAutoreleasePool alloc] init];  

   //。。。。需要做的处理。  

   //这里线程结束后立即返回  

  [self performSelectorOnMainThread:@selector(endThread) withObject:nil waitUntilDone:NO];  

  [pool release];  

}  

performSelectorOnMainThread通知主线程执行函数endThread。

再次强调子线程内不要进行任何UI操作,不要刷新界面。如果需要进行这些操作,通过dispatch_async或performSelectorOnMainThread这两种方法,调出主线程中的方法去执行。

--------------------------------------------

IOS开发(62)之GCD上异步执行非UI任务

分类: IOS开发 2013-05-10 16:27 869人阅读 评论(0) 收藏 举报

iOSGCD异步下载文件

1 前言

如果想要在 GCD 的帮助下能够异步执行 Non-UI 相关任务。在主队列、串行队列和并发队列上异步执行代码块才能见识到 GCD 的真正实力。

要在分派队列上执行异步任务,你必须使用下面这些函数中的其中一个:

dispatch_async

为了异步执行向分派队列提交一个 Block Object(2 个都通过函数指定);

eg:dispatch_sync(concurrentQueue, printFrom1To1000);

dispatch_async_f

为了异步执行向分派队列提交一个 C 函数和一个上下文引用(3 项通过函数指定) 。

eg:dispatch_sync_f(concurrentQueue,NULL,printFrom1To1000);

2 代码实例

这节代码有点多,所以分了两个工程

First:ZYViewController.m

[plain] view plaincopy

  1. - (void) viewDidAppear:(BOOL)paramAnimated{
  2. //新建一个队列
  3. dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  4. //执行concurrentQueue队列
  5. dispatch_async(concurrentQueue, ^{
  6. __block UIImage *image = nil;
  7. dispatch_sync(concurrentQueue, ^{
  8. /*下载图片*/
  9. /* 声明图片的路径*/
  10. NSString *urlAsString = @"http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg";
  11. //转换为NSURL对象
  12. NSURL *url = [NSURL URLWithString:urlAsString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
  13. //声明NSError对象:一个NSError对象封装错误信息更丰富、更具可扩展性可以只使用一个错误代码和错误字符串。
  14. NSError *downloadError = nil;
  15. //获得对应的Url返回的数据(这里是一个图片的数据)
  16. NSData *imageData = [NSURLConnection
  17. sendSynchronousRequest:urlRequest returningResponse:nil error:&downloadError];
  18. if (downloadError == nil &&imageData != nil){
  19. //将NSData转换为图片
  20. image = [UIImage imageWithData:imageData]; /* We have the image. We can use it now */
  21. }
  22. else if (downloadError != nil){
  23. NSLog(@"Error happened = %@", downloadError);
  24. }else{
  25. NSLog(@"No data could get downloaded from the URL.");
  26. }
  27. });
  28. dispatch_sync(dispatch_get_main_queue(), ^{
  29. /* 在主线程里面显示图片*/
  30. if (image != nil){
  31. /* 穿件UIImageView视图 */
  32. UIImageView *imageView = [[UIImageView alloc]
  33. initWithFrame:self.view.bounds];
  34. /* 设置Image */
  35. [imageView setImage:image];
  36. /* 内容适应视图的大小通过保持长宽比*/
  37. [imageView setContentMode:UIViewContentModeScaleAspectFit];
  38. /* 想Controller View添加图像视图 */
  39. [self.view addSubview:imageView];
  40. } else {
  41. NSLog(@"Image isn‘t downloaded. Nothing to display.");
  42. } });
  43. });
  44. }

运行结果

Second:

ZYViewController.h

[plain] view plaincopy

  1. #import <UIKit/UIKit.h>
  2. @interface ZYViewController : UIViewController
  3. @property(nonatomic,strong) UILabel *myLabel;
  4. @end

ZYViewController.m

[plain] view plaincopy

  1. @synthesize myLabel;
  2. - (void)viewDidLoad
  3. {
  4. [super viewDidLoad];
  5. //声明一个队列
  6. dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  7. /*
  8. 如果我们还没有保存1000个随机数在磁盘上,下面的队列就用来生成这个文件并且用一个Array存放在磁盘上
  9. */
  10. dispatch_async(concurrentQueue, ^{
  11. NSUInteger numberOfValuesRequired = 10000;
  12. //判断文件是否存在
  13. if ([self hasFileAlreadyBeenCreated]== NO){
  14. dispatch_sync(concurrentQueue, ^{
  15. //声明一个可变数组用来存放数值
  16. NSMutableArray *arrayOfRandomNumbers =[[NSMutableArray alloc] initWithCapacity:numberOfValuesRequired];
  17. NSUInteger counter = 0;
  18. for (counter = 0;counter < numberOfValuesRequired;counter++){
  19. //获得随机数
  20. unsigned int randomNumber =arc4random() % ((unsigned int)RAND_MAX + 1);
  21. [arrayOfRandomNumbers addObject:[NSNumber numberWithUnsignedInt:randomNumber]];
  22. }
  23. //将这个array写入到磁盘上
  24. [arrayOfRandomNumbers writeToFile:[self fileLocation] atomically:YES];
  25. });
  26. }
  27. //存放读取文件内容的数组
  28. __block NSMutableArray *randomNumbers = nil;
  29. //从磁盘上读取文件并升序排列
  30. dispatch_sync(concurrentQueue, ^{
  31. //如果文件已经被创建,读取他
  32. if ([self hasFileAlreadyBeenCreated]){
  33. randomNumbers = [[NSMutableArray alloc] initWithContentsOfFile:[self fileLocation]];
  34. /* 排序 */
  35. [randomNumbers sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
  36. {
  37. NSNumber *number1 = (NSNumber *)obj1;
  38. NSNumber *number2 = (NSNumber *)obj2;
  39. return [number1 compare:number2];
  40. }];
  41. }
  42. });
  43. dispatch_async(dispatch_get_main_queue(), ^{
  44. if ([randomNumbers count] > 0){
  45. /* 刷新主线程 */
  46. CGRect labelFrame = CGRectMake(0.0f, 0.0f, 300.0f, 200.0f);
  47. self.myLabel = [[UILabel alloc] initWithFrame:labelFrame];
  48. self.myLabel.numberOfLines = 10;//分10行
  49. NSString *str = [[NSString alloc] initWithFormat:@"RandomNumbers is %@",randomNumbers];//方法一
  50. self.myLabel.text = str;//label的文字
  51. self.myLabel.font = [UIFont boldSystemFontOfSize:14.0f];//字体样式
  52. self.myLabel.center = self.view.center;//UILabel控件居中
  53. [self.view addSubview:self.myLabel];
  54. }
  55. });
  56. });
  57. }
  58. //获得文件路径
  59. -(NSString *)fileLocation{
  60. /*
  61. 创建一个列表的目录搜索路径。
  62. NSDocumentDirectory:文档目录。
  63. NSUserDomainMask:用户的主目录的地方,存放用户的个人项目。
  64. */
  65. NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
  66. /* 如果没有找到返回空 */
  67. if ([folders count] == 0)
  68. {return nil; }
  69. /* 获得文件路径的字符串形式 */
  70. NSString *documentsFolder = [folders objectAtIndex:0];
  71. //将文件名追加到foldser后面
  72. return [documentsFolder stringByAppendingPathComponent:@"list.txt"];
  73. }
  74. //判断文件是否被存在
  75. - (BOOL) hasFileAlreadyBeenCreated{
  76. BOOL result = NO;
  77. //初始化NSFileManager文件管理对象
  78. NSFileManager *fileManager = [[NSFileManager alloc] init];
  79. //判断文件是否存在
  80. if ([fileManager fileExistsAtPath:[self fileLocation]])
  81. {
  82. result = YES;
  83. }
  84. return result;
  85. }

运行结果

时间: 2024-12-14 19:35:56

在多线程中进行UI操作的相关文章

多线程中简单的++操作,所引发的思考

一句简单的g_nLoginCount++操作,转换成汇编语言就成了上面的三句话,假如现在我们有两个线程,当第一个线程执行到第二个汇编时,此时第二个线程启动,他又从内存中读取g_nLoginCount,但这时第一个线程已经将g_nLoginCount做了加法操作,只是没有将其移回内存,这样的话这个加法操作形同虚设,这样计算的结果是不可预知的!!!! 多线程中简单的++操作,所引发的思考

winform中如何在多线程中更新UI控件--ListView实时显示执行信息

1.在winform中,所有对UI的操作,都得回到UI线程(主线程)上来,才不会报错 线程间操作无效: 从不是创建控件的线程访问它. 2.在winform中,允许通过Control.invoke对控件进行操作.如下代码: private void btnTest_Click(object sender, EventArgs e) { CheckA(); } private void CheckA() { System.Threading.ThreadPool.QueueUserWorkItem(

Android开发之在子线程中更新UI

转自第一行代码-Android Android是不允许在子线程中进行UI操作的.在子线程中去执行耗时操作,然后根据任务的执行结果来更新相应的UI控件,需要用到Android提供的异步消息处理机制. 代码如下: 1 public class MainActivity extends Activity implements OnClickListener { 2 private static final int UPDATE_TEXT=1; 3 private TextView textView;

基础篇-在非UI线程中更新UI元素

个人原创,转载请注明出处: http://blog.csdn.net/supluo/article/details/ 先了解两个概念 1.UI:User Interface的缩写,用户界面的意思.你可以不恰当的理解为我们能够看到的,操作的东西:在Android中什么才称为UI呢,可以简单的理解为View及其子类等元素.这是一个不够正确的概念,只是对新手做一个简单的抛砖引玉. 2.ANR:Application Not Responding,意思是程序没有响应. 在如下情况下,Android会报出

Qt中如何禁掉所有UI操作以及注意事项(处理各个widget的eventFilter这一层,但是感觉不好,为什么不使用QApplication呢)

刚做完的一个项目,在测试时出现了一个问题:由于多线程的存在,当进行语音识别时:如果用户点击程序界面上的button或者其他接受点击事件后会发出信号的widget时,程序会crash ! 后来尝试着从多线程上去解决,但是比较困难:后来只能从另外一条路来解决,那就是:当语音识别进行时:禁掉一切用户操作! 所谓的禁掉一切UI操作,在手机等手持设备上,尤其是纯触摸屏的设备上,主要就是指的禁止mouse操作!当然了:也可能是禁止键盘操作等.那如何去做这一点呢? 方法:我们可以截获禁止操作的窗口的所有eve

网络操作不能直接写在主线程中 以及 为什么不能在子线程中更新UI控件的属性

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //注意: 所有网络操作不能直接写在主线程中 因为所有的网络操作都是耗时的,如果加载到主线程中,会导致与用户的交互出现问题 ,所以要加载到子线程中 // [self loadImage]; [self performSelectorInBackground:@selector(loadImage) withObject:nil]; } //加

【基础】多线程更新窗体UI的若干方法

一.前言 在单线程中设置窗体某个控件的值很简单的事,只需要设置控件文本的值就可以了,但是有的业务场景很是复杂,界面上的控件也很多,这种情况下当数据量比较多的时候,在单线程中更新UI不可避免地会发生假死或卡顿现象,用户体验十分不爽,所以必须采用多线程来处理数据和UI.但是如果直接添加一个线程来更新控件信息,就会抛出错误,很显然微软并不希望我们这样做,因为UI控件不是线程安全的,如果随意地在任何线程中改变控件的值,会发生各种奇怪的问题,多个线程间会争夺资源,没有秩序地更改控件的值,显然这是我们不想看

android子线程中更新UI的方法

在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 参考:Android子线程 方法一:用Handler 1.主线程中定义Handler: Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: //

谨慎使用多线程中的fork

前言 在单核时代,大家所编写的程序都是单进程/单线程程序.随着计算机硬件技术的发展,进入了多核时代后,为了降低响应时间,重复充分利用多核cpu的资源,使用多进程编程的手段逐渐被人们接受和掌握.然而因为创建一个进程代价比较大,多线程编程的手段也就逐渐被人们认可和喜爱了. 记得在我刚刚学习线程进程的时候就想,为什么很少见人把多进程和多线程结合起来使用呢,把二者结合起来不是更好吗?现在想想当初真是too young too simple,后文就主要讨论一下这个问题. 进程与线程模型 进程的经典定义就是