在Objective-C中访问大文件时,会挂起程序的其余部分
嘿。 我对Objective-C for Mac有些陌生,我正在编写一个应用程序,可以打开大文件,解析它们,等等 问题是,当程序完成各种任务时,我希望更新进度条。但在每个任务完成之前,程序将变得无响应 我怎样才能避开这件事在Objective-C中访问大文件时,会挂起程序的其余部分,objective-c,cocoa,xcode,macos,io,Objective C,Cocoa,Xcode,Macos,Io,嘿。 我对Objective-C for Mac有些陌生,我正在编写一个应用程序,可以打开大文件,解析它们,等等 问题是,当程序完成各种任务时,我希望更新进度条。但在每个任务完成之前,程序将变得无响应 我怎样才能避开这件事 谢谢。您需要在后台线程上执行文件I/O和解析。在Cocoa/Objective-C中有几种方法可以做到这一点,最简单的方法可能是使用Grand Central Dispatch来执行带有Dispatch_async或类似函数的操作。有很多问题与使用GCD执行任务有关,所以,和
谢谢。您需要在后台线程上执行文件I/O和解析。在Cocoa/Objective-C中有几种方法可以做到这一点,最简单的方法可能是使用Grand Central Dispatch来执行带有Dispatch_async或类似函数的操作。有很多问题与使用GCD执行任务有关,所以,和是很好的起点
从第二篇文章中可以看出,有几种方法可以设置完成块,当给定操作在调度线程中完成时,完成块将在主线程上执行UI操作。如果您在主线程上执行处理器密集型任务(负责更新用户界面和处理键盘/鼠标事件),则您的应用程序可能会不负责任。使用Cocoa程序员可用的技术之一,将主线程从任何可能阻止它的东西中释放出来。这里是我最近编写的一个小示例,在新线程中执行任务,同时更新UI。基本上,只需用一些有用的代码替换[NSThread sleepForTimeInterval:0.2] 标题:
#import <Cocoa/Cocoa.h>
@interface ThreadingAppDelegate : NSObject <NSApplicationDelegate> {
@private
NSWindow *window;
NSSlider *slider;
NSProgressIndicator *progresIndicator;
NSTextField *textField;
NSButton *button;
NSButton *panicButton;
NSThread *aThread;
int theValue;
}
@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSSlider *slider;
@property (assign) IBOutlet NSProgressIndicator *progresIndicator;
@property (assign) IBOutlet NSTextField *textField;
@property (assign) IBOutlet NSButton *button;
@property (assign) IBOutlet NSButton *panicButton;
@property (assign) int theValue;
- (IBAction)startThread:(id)sender;
- (IBAction)stopThread:(id)sender;
- (void)moveStatusBar;
- (void)threadStart;
@end
实施:
#import "ThreadingAppDelegate.h"
@implementation ThreadingAppDelegate
@synthesize window;
@synthesize button;
@synthesize panicButton;
@synthesize slider;
@synthesize textField;
@synthesize progresIndicator;
@synthesize theValue;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)startThread:(id)sender
{
[progresIndicator setDoubleValue:0.0];
[button setEnabled:NO];
//[NSThread detachNewThreadSelector:@selector(threadStart) toTarget:self withObject:nil];
aThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadStart) object:nil];
[aThread start];
[panicButton setEnabled:YES];
}
- (IBAction)stopThread:(id)sender
{
[progresIndicator setDoubleValue:0.0];
[aThread cancel];
[aThread release];
aThread = nil;
[button setEnabled:YES];
[panicButton setEnabled:NO];
}
- (void)moveStatusBar
{
[progresIndicator setDoubleValue:[progresIndicator doubleValue] + 0.5];
if ([progresIndicator doubleValue] == 100) {
[button setEnabled:YES];
[panicButton setEnabled:NO];
}
}
- (void)threadStart
{
while([progresIndicator doubleValue] <= 100.0) {
[self performSelectorOnMainThread:@selector(moveStatusBar) withObject:nil waitUntilDone:NO];
[NSThread sleepForTimeInterval:0.2];
if([[NSThread currentThread] isCancelled]) {
break;
}
}
}
@end
用户界面:
背景线索并不总是正确的答案 如果您的任务是CPU受限的,那么后台线程是有意义的,但是您必须处理线程安全问题 如果您的任务是I/O绑定的,那么使用基于事件的API将更有意义,它将在数据可用时通知您。您可以将所有内容保留在主线程上,无需借助技巧来更新UI,而不会阻塞主运行循环 请参阅NSFileHandle的readInBackgroundAndNotify以获取示例:它允许操作系统填充缓冲区,然后在新数据可用时发布通知。在观察方法中,您可以执行以下操作:
- (void)dataReceived:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
NSData *newData = [info objectForKey:NSFileHandleNotificationDataItem];
[allData appendData:newData];
[progressIndicator setValue:([allData length] / totalFileLength)];
}
理想情况下,您应该边走边解析,而不是将所有新数据都塞进内存,但这对于您的特定数据结构可能不可行。感谢您的帮助,这些链接非常有用。做了我需要的。哇,这是一个很棒的答案。很抱歉,这有点太慢了,我觉得马提亚删除他的“答案”状态很刻薄,但这是一个很好的例子,它帮助了很多: