Абстрактный класс в Objective-C

Мне требуется ваша помощь. Следующая проблема в Objective-C:

// Robot.h
@protocol RobotProtocol <NSObject>
    -(void)doWork;
@end

@interface Robot : NSObject 


// Rob1 sublass of class Robot
// rob1.h
@interface Rob1 : Robot <RobotProtocol>

// rob1.m
@implementation
-(void)doWork
{
    // print 'robot 1'
}


// Rob2 sublass of class Robot
// rob2.h
@interface Rob2 : Robot <RobotProtocol>

// rob2.m
@implementation
-(void)doWork
{
    // print 'robot 2'
}



// Task.h
@interface Task : NSObject
{
    Robot *rob;
}


// Task.m
@implementation
- (id)init
{
    if ([super init]) {
        rob = [[Rob1 alloc] init]; // or Rob2 !!
    }
    return self;
}

-(void)doSomething
{
    [rob doWork]; // how to make sure, that this is implemented, depending on Rob1 or Rob2
}

Как следует реализовать Robot и его подклассы, чтобы Robot *rob мог быть одним из подклассов Robot rob1, rob2, ... и можно было вызвать метод doWork:(BOOL)val;? Моей первой идеей было реализовать Robot как абстрактный класс, но, к сожалению, в Objective-C нет абстрактных классов ...

На данный момент я использую протокол, но не уверен. Поскольку нет уверенности в том, что doWork реализован, компилятор жалуется на

"Робот" может не реагировать на "doWork"

Спасибо за идеи.


person Michael Dorner    schedule 07.12.2012    source источник
comment
Не могли бы вы опубликовать свой полный код Objective C? И определение, и использование?   -  person sergio    schedule 07.12.2012
comment
Вы пробовали реализовать методы с ложными утверждениями, чтобы они срабатывали во время выполнения? Не так хорошо, как проверка во время компиляции, но это может, по крайней мере, заставить вас переопределить.   -  person RonaldBarzell    schedule 07.12.2012
comment
Выбросить исключение в методе инициализации.   -  person Ramy Al Zuhouri    schedule 08.12.2012


Ответы (5)


Протоколы должны работать.

@protocol RobotProtocol <NSObject>
@required
- (void)doWork:(BOOL)flag;
@end

@interface Robot1 : NSObject <RobotProtocol>
@end

@implementation Robot1
- (void)doWork:(BOOL)flag
{
}
@end

Модульный тест для Robot1 вызван, хотя id<RobotProtocol>

- (void)testRobot
{
    id<RobotProtocol> robot = [[Robot1 alloc] init];
    [robot doWork:YES];
}

Обновить

После просмотра вашего кода @interface Robot : NSObject должно быть @interface Robot : NSObject <RobotProtocol>. Дело в том, что вам совсем не нужен @interface Robot : NSObject <RobotProtocol>. Вы можете просто использовать id<RobotProtocol>. Это имя вашего абстрактного класса.

person Jeffery Thomas    schedule 07.12.2012
comment
Спасибо. Я пробовал это, но моя ошибка заключалась в том, чтобы объявить в моем классе задачи id<RobotProtocol> *robot (с *), и это не работает! - person Michael Dorner; 07.12.2012

Как многие заявили, в Objective-C нет абстрактных классов. Лично я иду за комментариями + проверкой времени выполнения. Код вроде этого:

@interface Abstract : NSObject {
}

// Abstract method
- (Foo*)doSomething;

@end

@implementation Abstract

// Abstract method
- (Foo*)doSomething {
  [_self doesntRecognizeSelector:_cmd];
  return nil;
}

@end

Если вы действительно хотите использовать что-то «абстрактное» и проверять время компиляции, я думаю, вам придется использовать протоколы. Для этого вам нужно немного изменить свой код. В частности, вы должны объявить свою переменную соответствующей протоколу, id<RobotProtocol> или Robot<RobotProtocol>* в зависимости от того, что лучше соответствует вашим потребностям:

// Task.h
@interface Task : Robot <RobotProtocol>
{
    id<RobotProtocol> rob;
}
person SergGr    schedule 07.12.2012

Я предполагаю, что вы пытаетесь сделать что-то подобное, но без кода сложно сказать ...

//Robot.h
{
    -(void)doWork:(BOOL)myBool
}

//Robot.m
{
    -(void)doWork:(BOOL)myBool
    {
        if (myBool)
        {
            //do something
        }
        else
        {
            //do something else
        }
    }
}



//Task.h
{
       #include"Robot.h"
}

//Task.m
{
            Robot *rob = [[Robot rob] alloc] init];
    [rob doWork:YES];
}
person Dan    schedule 07.12.2012

Objective-C не имеет так называемых абстрактных классов, как в java. Просто создайте суперкласс под названием Robot и подклассифицируйте его ... потенциально вы можете даже заблокировать создание экземпляра класса, переопределив init и немедленно уничтожив себя (хотя я не слишком уверен в «корректности» такого подхода ..)

В любом случае это не должно быть проблемой.

Также удобный трюк, чтобы выяснить, реагирует ли определенный объект на метод, заключается в использовании

if( [rob respondsToSelector:@selector(doWork:)] ) { [rob doWork:value]; }

это часто используется для того, чтобы сделать методы в протоколе необязательными, чтобы вы не получали ошибок во время выполнения, когда вы вызываете метод, который не существует для данного объекта.

person Antwan van Houdt    schedule 07.12.2012
comment
Спасибо за идею, но ее следует проверить во время компиляции. - person Michael Dorner; 07.12.2012

Знает ли какой-либо метод, который пытается вызвать doWork, о протоколе RobotProtocol? Возможно, вам потребуется импортировать любой файл заголовка, который его содержит.

Как бы то ни было, общепринятый способ делать то, что вы хотите, - просто объявить то, что является суперклассом (робот), объявить и реализовать методы с пустыми телами (возможно, выбросить NSException, если они не перезаписаны подклассом) и создать свой подкласс (SubRobot, или что у вас). Протоколы в большей степени предназначены для того, чтобы различные типы классов могли реализовывать несколько известных методов, а не для использования в качестве абстрактных суперклассов де-факто.

person Max    schedule 07.12.2012