在Objective-C中访问大文件时,会挂起程序的其余部分

在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执行任务有关,所以,和

嘿。 我对Objective-C for Mac有些陌生,我正在编写一个应用程序,可以打开大文件,解析它们,等等

问题是,当程序完成各种任务时,我希望更新进度条。但在每个任务完成之前,程序将变得无响应

我怎样才能避开这件事


谢谢。

您需要在后台线程上执行文件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)];
}

理想情况下,您应该边走边解析,而不是将所有新数据都塞进内存,但这对于您的特定数据结构可能不可行。

感谢您的帮助,这些链接非常有用。做了我需要的。哇,这是一个很棒的答案。很抱歉,这有点太慢了,我觉得马提亚删除他的“答案”状态很刻薄,但这是一个很好的例子,它帮助了很多: