我有一个iPad应用程序,我想这样做
-(IBAction) clicked {
image=download(@"http://....."); // this is on the main thread
}
下载功能将调用一大堆非阻塞功能从互联网上下载文件,但在下载图像之前,下载本身不应该返回。
当程序在上面的image = download(...)行等待下载时,我希望UI能够仍然运行,例如能够滚动UITableView,点击另一个按钮等。 / p>
所以我在下载函数中使用的是RunLoop
-(void) download:(NSString *)url
{
BOOL stillDownloading=TRUE;
while(stillDownloading) {
stillDownloading=downloadAFwBytes(...);
CFRunLoopRunInMode(kCFRunLoopCommonModes, 0, YES);
}
}
我认为CFRunLoopRunInMode函数将继续抽取UI消息,触摸,滚动主UI线程,以便UI继续工作,直到下载完成后才冻结,但由于某种原因,它只能工作很短的时间,最终UI冻结了。
你知道为什么,或者如何解决?
下载功能在程序的任何地方都会被调用,期望它等待下载,所以我暂时无法将其更改为非阻塞。
答案 0 :(得分:5)
您问题的直接答案是,不,这不是CFRunLoopRunInMode
所做的。你有效地尝试做的是使当前的运行循环“yield”,以便在加载操作继续时继续执行。这不是iOS和运行循环的工作方式。您的下载功能会阻止其下载的线程,直到下载完成为止,因此您的问题的唯一解决方案是更改实施,以便在后台线程上进行下载,并在完成时通知关注的对象。这是一个相对较小的变化,可以让你走上正确的轨道。这个整体主题(并发,管理后台任务)是一个更大的讨论,有不同的考虑/权衡。我会切入追逐,希望能让你走上正轨。
定义一些NSNotification
,您的下载方法可以发布以供感兴趣的对象观察:
// in the .h file of the class performing the download
extern NSString * const MyClassLoadingDidStartNotification;
extern NSString * const MyClassLoadingDidFinishNotification;
// in the .m file of the class performing the download
NSString * const MyClassLoadingDidStartNotification = @"MyClassLoadingDidStart";
NSString * const MyClassLoadingDidFinishNotification = @"MyClassLoadingDidFinish";
在下载例程中,请在后台进行下载并发布相应的通知:
-(void) download:(NSString *)url
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidStartNotification object:self];
BOOL stillDownloading=TRUE;
while(stillDownloading) {
stillDownloading=downloadAFwBytes(...);
}
[[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidFinishNotification object:self];
});
}
在任何启动下载的对象中,观察并处理通知
// in any class that initiates a download
- (void)init...
{
self = [super init...];
if (self) {
// other initialization
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartLoading:) name:MyClassLoadingDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLoading:) name:MyClassLoadingDidFinishNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidFinishNotification object:nil];
}
- (void)didStartLoading:(NSNotification *)notification
{
// update UI to show loading status (make sure you do UI changes on main thread)
// optionally check notification.object to ensure it's the loader class instance you care about
}
- (void)didFinishLoading:(NSNotification *)notification
{
// update UI to show loading status (make sure you do UI changes on main thread)
// optionally check notification.object to ensure it's the loader class instance you care about
}
请记住,这是一个非常基本的起点。随着您了解更多并决定您需要什么,您一定会定制它。例如,您可能希望添加错误处理,限制并发加载操作,提供其他加载状态等。
答案 1 :(得分:0)
您的问题存在一些架构问题,但要在后台正确下载图像并将其加载到UIImageView或以任何其他方式使用它,我建议您查看AFNetworking并阅读通过他们的示例代码下载资源。
答案 2 :(得分:0)
您可以请求在后台下载图片,然后在您的方法中尽快返回静态图片吗?
这样,主线程快速返回,并且根据您的体系结构,后台线程一旦获得它就可以更新图像?
我认为我们需要有关您的代码的更多详细信息(您的下载方法无效,以及如何使用下载的数据?)才能真正提供帮助。
答案 3 :(得分:0)
与其他3个人告诉你同样的事情,我会告诉你忘记你自己的运行循环模型,并且只使用异步下载功能(假设你是通过网络下载的)。你甚至不需要构建一个线程,只需启动异步下载,它们会告诉你它们何时完成。如果您没有时间对其进行编码,您何时有时间修改代码?
答案 4 :(得分:0)
不确定这是否已解决,但你不能使用?
[self.operationQueue addOperationWithBlock:^{
[self MethodName];
}];
当你想在UI中发生类似表更新的事情时,请将其放在方法代码中:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
////perform something here ////
}];
希望这会有所帮助。