在多线程中进行UI操作
2015-01-08 10:56
405 查看
转载自 /article/1348961.html
iOS 上不建议在非主线程进行UI操作,在非主线程进行UI操作有很大几率会导致程序崩溃,或者出现预期之外的效果。
我开始不知道这一点,在子线程中进行了弹窗操作,结果程序就出问题了!
报的错误是(EXC_BAD_ACCESS(code=2,address=0xcc),0x1a0ad32: movl 204(%ecx), %edx ),我以为是空指针导致的内存泄露,用了很多方法,但这问题感觉很顽固,困扰了我很多天。
后来有位大牛指点了我,问我是不是在子线程进行这个弹窗操作。。。直到此时我才明白问题出在哪里,问题顺利解决。有时候出现bug却不知道是哪引起的,这时是最纠结的,等明确了问题所在,问题就不是问题了。好了,言归正传。
那么在子线程中的UI操作如何处理呢?有两种方法:
一:在子线程,你需要进行的UI操作前添加dispatch_async函数,即可将代码块中的工作转回到主线程
dispatch_async函数是异步操作,对于比较耗时的操作也可以这样处理:
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];
performSelectorOnMainThread通知主线程执行函数endThread。
再次强调子线程内不要进行任何UI操作,不要刷新界面。如果需要进行这些操作,通过dispatch_async或performSelectorOnMainThread这两种方法,调出主线程中的方法去执行。
--------------------------------------------
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
- (void) viewDidAppear:(BOOL)paramAnimated{
//新建一个队列
dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//执行concurrentQueue队列
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
dispatch_sync(concurrentQueue, ^{
/*下载图片*/
/* 声明图片的路径*/
NSString *urlAsString = @"http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg";
//转换为NSURL对象
NSURL *url = [NSURL URLWithString:urlAsString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
//声明NSError对象:一个NSError对象封装错误信息更丰富、更具可扩展性可以只使用一个错误代码和错误字符串。
NSError *downloadError = nil;
//获得对应的Url返回的数据(这里是一个图片的数据)
NSData *imageData = [NSURLConnection
sendSynchronousRequest:urlRequest returningResponse:nil error:&downloadError];
if (downloadError == nil &&imageData != nil){
//将NSData转换为图片
image = [UIImage imageWithData:imageData]; /* We have the image. We can use it now */
}
else if (downloadError != nil){
NSLog(@"Error happened = %@", downloadError);
}else{
NSLog(@"No data could get downloaded from the URL.");
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* 在主线程里面显示图片*/
if (image != nil){
/* 穿件UIImageView视图 */
UIImageView *imageView = [[UIImageView alloc]
initWithFrame:self.view.bounds];
/* 设置Image */
[imageView setImage:image];
/* 内容适应视图的大小通过保持长宽比*/
[imageView setContentMode:UIViewContentModeScaleAspectFit];
/* 想Controller View添加图像视图 */
[self.view addSubview:imageView];
} else {
NSLog(@"Image isn't downloaded. Nothing to display.");
} });
});
}
运行结果
Second:
ZYViewController.h
[plain] view plaincopy
#import <UIKit/UIKit.h>
@interface ZYViewController : UIViewController
@property(nonatomic,strong) UILabel *myLabel;
@end
ZYViewController.m
[plain] view plaincopy
@synthesize myLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
//声明一个队列
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
如果我们还没有保存1000个随机数在磁盘上,下面的队列就用来生成这个文件并且用一个Array存放在磁盘上
*/
dispatch_async(concurrentQueue, ^{
NSUInteger numberOfValuesRequired = 10000;
//判断文件是否存在
if ([self hasFileAlreadyBeenCreated]== NO){
dispatch_sync(concurrentQueue, ^{
//声明一个可变数组用来存放数值
NSMutableArray *arrayOfRandomNumbers =[[NSMutableArray alloc] initWithCapacity:numberOfValuesRequired];
NSUInteger counter = 0;
for (counter = 0;counter < numberOfValuesRequired;counter++){
//获得随机数
unsigned int randomNumber =arc4random() % ((unsigned int)RAND_MAX + 1);
[arrayOfRandomNumbers addObject:[NSNumber numberWithUnsignedInt:randomNumber]];
}
//将这个array写入到磁盘上
[arrayOfRandomNumbers writeToFile:[self fileLocation] atomically:YES];
});
}
//存放读取文件内容的数组
__block NSMutableArray *randomNumbers = nil;
//从磁盘上读取文件并升序排列
dispatch_sync(concurrentQueue, ^{
//如果文件已经被创建,读取他
if ([self hasFileAlreadyBeenCreated]){
randomNumbers = [[NSMutableArray alloc] initWithContentsOfFile:[self fileLocation]];
/* 排序 */
[randomNumbers sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
NSNumber *number1 = (NSNumber *)obj1;
NSNumber *number2 = (NSNumber *)obj2;
return [number1 compare:number2];
}];
}
});
dispatch_async(dispatch_get_main_queue(), ^{
if ([randomNumbers count] > 0){
/* 刷新主线程 */
CGRect labelFrame = CGRectMake(0.0f, 0.0f, 300.0f, 200.0f);
self.myLabel = [[UILabel alloc] initWithFrame:labelFrame];
self.myLabel.numberOfLines = 10;//分10行
NSString *str = [[NSString alloc] initWithFormat:@"RandomNumbers is %@",randomNumbers];//方法一
self.myLabel.text = str;//label的文字
self.myLabel.font = [UIFont boldSystemFontOfSize:14.0f];//字体样式
self.myLabel.center = self.view.center;//UILabel控件居中
[self.view addSubview:self.myLabel];
}
});
});
}
//获得文件路径
-(NSString *)fileLocation{
/*
创建一个列表的目录搜索路径。
NSDocumentDirectory:文档目录。
NSUserDomainMask:用户的主目录的地方,存放用户的个人项目。
*/
NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
/* 如果没有找到返回空 */
if ([folders count] == 0)
{return nil; }
/* 获得文件路径的字符串形式 */
NSString *documentsFolder = [folders objectAtIndex:0];
//将文件名追加到foldser后面
return [documentsFolder stringByAppendingPathComponent:@"list.txt"];
}
//判断文件是否被存在
- (BOOL) hasFileAlreadyBeenCreated{
BOOL result = NO;
//初始化NSFileManager文件管理对象
NSFileManager *fileManager = [[NSFileManager alloc] init];
//判断文件是否存在
if ([fileManager fileExistsAtPath:[self fileLocation]])
{
result = YES;
}
return result;
}
运行结果
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
- (void) viewDidAppear:(BOOL)paramAnimated{
//新建一个队列
dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//执行concurrentQueue队列
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
dispatch_sync(concurrentQueue, ^{
/*下载图片*/
/* 声明图片的路径*/
NSString *urlAsString = @"http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg";
//转换为NSURL对象
NSURL *url = [NSURL URLWithString:urlAsString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
//声明NSError对象:一个NSError对象封装错误信息更丰富、更具可扩展性可以只使用一个错误代码和错误字符串。
NSError *downloadError = nil;
//获得对应的Url返回的数据(这里是一个图片的数据)
NSData *imageData = [NSURLConnection
sendSynchronousRequest:urlRequest returningResponse:nil error:&downloadError];
if (downloadError == nil &&imageData != nil){
//将NSData转换为图片
image = [UIImage imageWithData:imageData]; /* We have the image. We can use it now */
}
else if (downloadError != nil){
NSLog(@"Error happened = %@", downloadError);
}else{
NSLog(@"No data could get downloaded from the URL.");
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* 在主线程里面显示图片*/
if (image != nil){
/* 穿件UIImageView视图 */
UIImageView *imageView = [[UIImageView alloc]
initWithFrame:self.view.bounds];
/* 设置Image */
[imageView setImage:image];
/* 内容适应视图的大小通过保持长宽比*/
[imageView setContentMode:UIViewContentModeScaleAspectFit];
/* 想Controller View添加图像视图 */
[self.view addSubview:imageView];
} else {
NSLog(@"Image isn't downloaded. Nothing to display.");
} });
});
}
运行结果
Second:
ZYViewController.h
[plain] view plaincopy
#import <UIKit/UIKit.h>
@interface ZYViewController : UIViewController
@property(nonatomic,strong) UILabel *myLabel;
@end
ZYViewController.m
[plain] view plaincopy
@synthesize myLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
//声明一个队列
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
如果我们还没有保存1000个随机数在磁盘上,下面的队列就用来生成这个文件并且用一个Array存放在磁盘上
*/
dispatch_async(concurrentQueue, ^{
NSUInteger numberOfValuesRequired = 10000;
//判断文件是否存在
if ([self hasFileAlreadyBeenCreated]== NO){
dispatch_sync(concurrentQueue, ^{
//声明一个可变数组用来存放数值
NSMutableArray *arrayOfRandomNumbers =[[NSMutableArray alloc] initWithCapacity:numberOfValuesRequired];
NSUInteger counter = 0;
for (counter = 0;counter < numberOfValuesRequired;counter++){
//获得随机数
unsigned int randomNumber =arc4random() % ((unsigned int)RAND_MAX + 1);
[arrayOfRandomNumbers addObject:[NSNumber numberWithUnsignedInt:randomNumber]];
}
//将这个array写入到磁盘上
[arrayOfRandomNumbers writeToFile:[self fileLocation] atomically:YES];
});
}
//存放读取文件内容的数组
__block NSMutableArray *randomNumbers = nil;
//从磁盘上读取文件并升序排列
dispatch_sync(concurrentQueue, ^{
//如果文件已经被创建,读取他
if ([self hasFileAlreadyBeenCreated]){
randomNumbers = [[NSMutableArray alloc] initWithContentsOfFile:[self fileLocation]];
/* 排序 */
[randomNumbers sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
NSNumber *number1 = (NSNumber *)obj1;
NSNumber *number2 = (NSNumber *)obj2;
return [number1 compare:number2];
}];
}
});
dispatch_async(dispatch_get_main_queue(), ^{
if ([randomNumbers count] > 0){
/* 刷新主线程 */
CGRect labelFrame = CGRectMake(0.0f, 0.0f, 300.0f, 200.0f);
self.myLabel = [[UILabel alloc] initWithFrame:labelFrame];
self.myLabel.numberOfLines = 10;//分10行
NSString *str = [[NSString alloc] initWithFormat:@"RandomNumbers is %@",randomNumbers];//方法一
self.myLabel.text = str;//label的文字
self.myLabel.font = [UIFont boldSystemFontOfSize:14.0f];//字体样式
self.myLabel.center = self.view.center;//UILabel控件居中
[self.view addSubview:self.myLabel];
}
});
});
}
//获得文件路径
-(NSString *)fileLocation{
/*
创建一个列表的目录搜索路径。
NSDocumentDirectory:文档目录。
NSUserDomainMask:用户的主目录的地方,存放用户的个人项目。
*/
NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
/* 如果没有找到返回空 */
if ([folders count] == 0)
{return nil; }
/* 获得文件路径的字符串形式 */
NSString *documentsFolder = [folders objectAtIndex:0];
//将文件名追加到foldser后面
return [documentsFolder stringByAppendingPathComponent:@"list.txt"];
}
//判断文件是否被存在
- (BOOL) hasFileAlreadyBeenCreated{
BOOL result = NO;
//初始化NSFileManager文件管理对象
NSFileManager *fileManager = [[NSFileManager alloc] init];
//判断文件是否存在
if ([fileManager fileExistsAtPath:[self fileLocation]])
{
result = YES;
}
return result;
}
运行结果
相关文章推荐
- 在多线程中进行UI操作--ios学习笔记
- 在多线程中进行UI操作
- !!c# 委托与多线程 -- 相比background来说,本方法针对当需要通过异步代理发起UI操作,而后线程中进行sleep操作的时使用
- 在多线程中进行UI操作--ios学习笔记
- 在多线程中进行UI操作--ios学习笔记
- 关于在非UI线程中进行UI操作会出现问题: Can't create handler inside thread that has not called Looper.prepare()
- 多线程与UI操作
- wpf中的多线程和窗口ui操作
- 多线程与UI操作
- 使用Thread包装类进行多线程操作
- JAVA多线程中,原子操作的概念——原子操作真的不需要进行同步控制吗?
- 多线程编程(4):多线程与UI操作
- 多线程操作UI
- android系统中的多线程(二): 关于在work thread中对UI进行更新和设置
- Winform中进行线程安全的多线程操作(主线程控件,非backgroundWorker)
- 多线程编程(4):多线程与UI操作 .
- BackgroundWorker组件进行异步操作UI
- 多线程编程(4):多线程与UI操作
- C#编程:多线程与UI操作
- C# 多线程编程(4):多线程与UI操作