NSTask subclass 错误 - launchPath 仅为抽象 class 定义

NSTask subclass error - launchPath only defined for abstract class

我正在尝试创建 NSTask 的子类,为了显示状态,它增加了 nameactivityDescription 属性, UI 在 OSX 桌面应用程序中。

但是,当我尝试在我的子类实例上设置 launchPatharguments 属性 时,出现以下错误:

launchPath only defined for abstract class. Define -[Task launchPath]!
arguments only defined for abstract class. Define -[Task arguments]!

所以,我定义了 setLaunchPath:setArguments: 如下所示,我仍然得到同样的错误。

对了,NSData stringValue定义在NSData+Additions.h:

- (NSString *) stringValue {
    return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}

非常感谢任何帮助!

Task.h

#import <Foundation/Foundation.h>

@protocol TaskDelegate;

@interface Task : NSTask {
    NSString *_launchTask;
    NSArray *_arguments;
}

@property (weak, nonatomic) id<TaskDelegate> delegate;

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *activityDescription;

//To be implemented by subclasses, should be called just before launch
- (void) setupTask;

- (BOOL) isConfigured;

//Launches the task - includes any setup required before the task is launched
- (void) launchAndWait;

- (void) setLaunchPath:(NSString *)launchPath;
- (void) setArguments:(NSArray *)arguments;

@end

@protocol TaskDelegate <NSObject>

- (void) task:(Task *)task didReceiveTaskError:(NSString *)errorString;

@end

Task.m

#import "Task.h"
#import "NSData+Additions.h"

@interface Task ()

@end

@implementation Task

- (id) init
{
    self = [super init];
    if (self) {
    }

    return self;
}

- (void) errorOccurred:(NSNotification *)notification {
    if (_delegate) {
        NSData *readData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
        NSString *outputString = [readData stringValue];
        if (outputString.length)
            [_delegate task:self didReceiveTaskError:outputString];
    }
}

- (void) setupTask
{
    //To be implemented by subclasses
}

- (BOOL) isConfigured
{
    return (self.launchPath != nil);
}

//Process task conguration and execution is optional - if process task is not configured, this returns immediately
- (void) launchAndWait
{
    //Allow setupTask to be used, without actually launching
    if (![self isConfigured])
        return;

    NSPipe *errorPipe = [NSPipe pipe];
    self.standardError = errorPipe;
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(errorOccurred:)
                                                 name:NSFileHandleReadToEndOfFileCompletionNotification
                                               object:[errorPipe fileHandleForReading]];
    [[errorPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];

    [self launch];
    [self waitUntilExit];

    //Tear down
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void) setLaunchPath:(NSString *)launchPath
{
    _launchTask = launchPath;
}

- (void) setArguments:(NSArray *)arguments
{
    _arguments = arguments;
}

@end

我决定使用组合方法并在我的 Task class 中使用 NSTask,而不是继承,并且它运行良好。

这是更新后的 class:

Task.h

#import <Foundation/Foundation.h>

@protocol TaskDelegate;

@interface Task : NSObject {
    __weak id<TaskDelegate> _delegate;
    NSTask *_processTask;
}

@property (weak, nonatomic) id<TaskDelegate> delegate;

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *activityDescription;

//To be called just before launch
- (void) setupTask;

- (BOOL) processTaskConfigured;

//Launches the task - includes any setup required before the task is launched
- (void) launchAndWait;

//////////////////////////////////////////////////////////////////////////////////////////////// Process task delegation
@property (readonly) int terminationStatus;

- (BOOL) isRunning;
- (void) terminate;

@end

@protocol TaskDelegate <NSObject>

- (void) task:(Task *)task didReceiveTaskError:(NSString *)errorString;

@end

Task.m

#import "Task.h"
#import "NSData+Additions.h"

@interface Task ()

//Delegating to NSTask
@property (strong, nonatomic) NSTask *processTask;

@end

@implementation Task

- (id) init
{
    self = [super init];
    if (self) {
        self.processTask = [NSTask new];
    }

    return self;
}

- (void) errorOccurred:(NSNotification *)notification {
    if (_delegate) {
        NSData *readData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
        NSString *outputString = [readData stringValue];
        if (outputString.length)
            [_delegate task:self didReceiveTaskError:outputString];
    }
}

- (void) setupTask
{
    //To be implemented by subclasses
}

- (BOOL) processTaskConfigured
{
    return (_processTask.launchPath != nil);
}

//Process task conguration and execution is optional - if process task is not configured, this returns immediately
- (void) launchAndWait
{
    if (!_processTask.launchPath)
        return;

    NSPipe *errorPipe = [NSPipe pipe];
    _processTask.standardError = errorPipe;
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(errorOccurred:)
                                                 name:NSFileHandleReadToEndOfFileCompletionNotification
                                               object:[errorPipe fileHandleForReading]];
    [[errorPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];

    [_processTask launch];
    [_processTask waitUntilExit];

    //Tear down
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

////////////////////////////////////////////////////////////////////////////////////////// Process task delegation

- (int) terminationStatus
{
    if ([self processTaskConfigured]) {
        return [_processTask terminationStatus];
    }

    return 0;
}

- (BOOL) isRunning
{
    return [_processTask isRunning];
}

- (void) terminate
{
    if ([self processTaskConfigured]) {
        [_processTask terminate];
    }
}

@end