Это циклическая ссылка?

У меня есть класс, который наследуется от CCSprite, который называется GameObject. У меня также есть синглтон с именем ActionDispatcher, который наследуется от NSObject.

Задача ActionDispatcher sharedActionDispatcher's состоит в том, чтобы возвращать CCActionInterval при запросе, и метод, который это делает, требует ссылки на запрашивающий объект, который имеет тип GameObject (или его подкласс). Но чтобы GameObject мог запросить это, ему нужно сослаться на ActionDispatcher. Итак, у меня есть #import "GameObject.h" в заголовке ActionDispatcher и #import "ActionDispatcher" в заголовке GameObject.

Я получаю сообщение об ошибке: Expected ')' before 'GameObject' в методе ActionManager, который принимает ссылку на GameObject.

РЕДАКТИРОВАТЬ: добавлены комментарии, чтобы показать, где я исправил это в соответствии с принятым ответом ниже.


//The GameObject interface

#import "cocos2d.h"
#import "ActionDispatcher.h"

@interface GameObject : CCSprite {
    CGPoint homeLocation;
}

@property (readwrite) CGPoint homeLocation;

- (void)updateStateWithDeltaTime:(ccTime)deltaTime 
        andListOfGameObjects:(CCArray*)listOfGameObjects;

@end

//The GameObject implementation

#import "GameObject.h"

@implementation GameObject

@synthesize homeLocation;

- (void)updateStateWithDeltaTime:(ccTime)deltaTime 
        andListOfGameObjects:(CCArray *)listOfGameObjects 
{
    //CCLOG(@"updateStateWithDeltaTime method should be overriden");
}

@end

//The ActionDispatcher interface

#import "cocos2d.h"
#import "CCRotateAround.h"
#import "Constants.h"
#import "GameObject.h" // Answer: Remove this line

// Add this here: @class GameObject;

@interface ActionDispatcher : NSObject {

}

+ (ActionDispatcher *)sharedActionDispatcher;

- (id)actionWithType:(ActionType)actionType 
          withObject:(GameObject *)gameObject 
        withDuration:(float)duration;

@end

//The ActionDispatcher implementation

#import "ActionDispatcher.h"
// and add this here: #import "GameObject.h"

@implementation ActionDispatcher

static ActionDispatcher* _sharedActionDispatcher = nil;

+ (ActionDispatcher*)sharedActionDispatcher 
{
    @synchronized([ActionDispatcher class])                             
    {
        if(!_sharedActionDispatcher)                                    
            [[self alloc] init]; 
        return _sharedActionDispatcher;                                 
    }
    return nil; 
}

+ (id)alloc 
{
    @synchronized ([ActionDispatcher class])                            
    {
        NSAssert(_sharedActionDispatcher == nil,
                 @"Attempted to allocated a second instance of the ActionManager singleton");                                          
        _sharedActionDispatcher = [super alloc];
        return _sharedActionDispatcher;                                 
    }
    return nil;  
}

- (id)actionWithType:(ActionType)actionType 
          withObject:(GameObject *)gameObject 
        withDuration:(float)duration
{
    CGSize screenSize = [[CCDirector sharedDirector] winSize];

    id action = nil;

    switch (actionType) {
        case kActionDiveBomb:
            CCLOG(@"ActionManager returning action of type: dive bomb");
            CGPoint controlPoint1 = ccp(gameObject.position.x, gameObject.position.y*0.5f);
            CGPoint controlPoint2 = ccp(screenSize.width*0.5f, gameObject.position.y*0.5f);
            CGPoint destination = ccp(screenSize.width*0.5f, -gameObject.contentSize.height*0.5f);
            ccBezierConfig diveBombBezier;
            diveBombBezier.controlPoint_1 = controlPoint1;
            diveBombBezier.controlPoint_2 = controlPoint2;
            diveBombBezier.endPosition = destination;
            id diveAction = [CCBezierTo actionWithDuration:duration bezier:diveBombBezier];
            id returnToTopAction = [CCMoveTo actionWithDuration:0.0f position:ccp(gameObject.homeLocation.x, screenSize.height+gameObject.contentSize.height/2)];
            id fallInAction = [CCMoveTo actionWithDuration:duration*0.2 position:gameObject.homeLocation];
            action = [CCSequence actions:diveAction, returnToTopAction, fallInAction, nil];
            break;
        case kActionLoop:
            CCLOG(@"ActionManager returning action of type: loop");
            CGPoint centerPoint = ccp(screenSize.width/2, screenSize.height/2);
            float span = 360.0;
            action = [CCRotateAround actionWithDuration:duration centerPoint:centerPoint spanAngle:span];
            break;
        case kActionSpin:
            CCLOG(@"ActionManager returning action of type: spin");
            action = [CCRotateBy actionWithDuration:duration angle:360.0f];
            break;
        case kActionGoHome:
            CCLOG(@"ActionManager returning action of type: go home");
            action = [CCMoveTo actionWithDuration:0.0f position:[gameObject homeLocation]];
            break;
        case kActionFallIn:
            CCLOG(@"ActionManager returning action of type: fall in");
            action = [CCMoveTo actionWithDuration:duration position:[gameObject homeLocation]];
            break;
        case kActionIdle:
            CCLOG(@"ActionManager returning action of type: idle");
            action = [CCDelayTime actionWithDuration:duration];
            break;
        default:
            CCLOG(@"ActionManager returning action of type: no valid action");
            break;
    }

    return action;
}

@end

person Steve    schedule 14.09.2011    source источник


Ответы (3)


Просто добавьте директиву @class в ActionDispatcher.h над директивой @interface. Это предварительное объявление класса, который импортируется в реализацию.

@class GameObject;

person Mark Adams    schedule 14.09.2011
comment
Да, и удалите импорт GameObject.h. Вместо этого импортируйте его в ActionDispatcher.m. - person Hot Licks; 14.09.2011

Возможный. Попробуйте вставить

@class GameObject

предварительная декларация после импорта в ActionManager.m? Это должно разорвать цикл.

person futureelite7    schedule 14.09.2011

Это не проблема. Директива #import автоматически обрабатывает включение уже включенных файлов. Обсуждение здесь. Чтобы проверить это, закомментируйте все остальные импорты, создайте подкласс вашего GameObject из NSObject, и он скомпилируется без ошибок. Ошибка говорит вам, что что-то не так в одном из файлов, которые вы включаете. Иногда это может быть неправильный символ в заголовочном файле после ключевого слова @end, из-за чего компилятор ругается не в том месте.

person Davyd Geyl    schedule 14.09.2011
comment
Это распространенное заблуждение. #import предотвращает многократное включение файлов, но это ничего не делает для циклических зависимостей. Если файлу A нужно, чтобы содержимое файла B было перед ним, а файлу B нужно, чтобы перед ним было содержимое файла A, все, что гарантирует уникальность #import, — это гарантия того, что один из этих файлов не исполнит свое желание, и то вы получите ошибку компиляции в этом файле. - person Chuck; 14.09.2011
comment
Спасибо за комментарий, Чак. Я понимаю вашу точку зрения. Однако я не могу воспроизвести ошибку таким образом. Я взял пример, когда ActionDispatcher включает GameObject в заголовок, а GameObject включает ActionDispatcher в свой заголовок. В реализации классы используют методы друг друга. Ошибок компиляции нет вообще. Не могли бы вы привести пример, как воспроизвести ошибку? - person Davyd Geyl; 14.09.2011
comment
Хорошо, я воспроизвел это. Я был неправ. Черт возьми! Такой ошибки у меня еще не было. Спасибо, парни! - person Davyd Geyl; 14.09.2011