Objective C - Error: 'Expected a type'

user2577959 picture user2577959 · Jul 13, 2013 · Viewed 24.7k times · Source

I'm getting a very strange error on something that I would have thought to be simple.

#import <Foundation/Foundation.h>
#import "ViewController.h"
#import "GameObject.h"


@interface GameController : NSObject 

@property (strong) GLKBaseEffect * effect;
@property (strong) NSMutableArray * gameObjects;
@property (strong) NSMutableArray * objectsToRemove;
@property (strong) NSMutableArray * objectsToAdd;


+ (GameController *) sharedGameController;
- (void) tick:(float)dt;
- (void) initializeGame: (ViewController*) viewcontroller;//ERROR: EXPECTED A TYPE

- (void) createObject:(Class) objecttype atPoint:(CGPoint)position;
- (void) deleteObject:(GameObject*) object atPoint:(CGPoint)position;
- (void) manageObjects;

@end

Why would it question whether or not 'ViewController' is a type? It's a class that I've correctly implemented. It has also been imported.

EDIT *

Here is the ViewController.m class if it helps.

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[GameController sharedGameController] initializeGame:self];
}

@end

EDIT 2 **

and the ViewController.h file

#import <GLKit/GLKit.h>
#import "GameController.h" 

@interface ViewController : GLKViewController

@end

Answer

justin picture justin · Jul 13, 2013

Use a forward declaration: @class ViewController; in place of #import "ViewController.h". The import is usually unnecessary in another header in Objective-C.

If you use ViewController in GameController, then you can add the import to GameController.m.

You probably have a circular dependency.

The basic way to define a circular dependency is:

  • GameController.h imports ViewController.h
  • ViewController.h imports GameController.h

Which one will be seen first depends on the order of declaration in the translation, but obviously one will have to come first because in this case the headers disagree as to which must come first.

In a real codebase, you may #import "ViewController.h" in many source files. This creates very complex dependencies. These dependencies are largely unnecessary in ObjC -- you can use forward declarations most of the time in a header (and it will improve your build times).

So I explained the simplest case, but what happens when 15 headers #import ViewController.h? Well, you would have to track down which header is introducing that circular dependency for that translation. If you don't manage dependencies well, then you may have to step through dozens (or hundreds) of files. Sometimes, it's easiest to just review the preprocessed output for that translation (e.g. the offending *.m file). If dependencies are not minimized, that output could be hundreds of thousands of lines (and your build times could be 20 or more times faster if it were managed correctly). So the complexity of locating circular dependencies quickly goes up in large codebases; the more you #import and #include in the headers. Using forward declarations in headers (where possible) solves this problem.

Example

So given your header in the OP, you could rewrite it as:

GameController.h

// includes
#import <Foundation/Foundation.h>

// forwards required by this header    
@class GameObject;
@class GLKBaseEffect;
@class ViewController;

// header declarations
@interface GameController : NSObject 

@property (strong) GLKBaseEffect * effect;
@property (strong) NSMutableArray * gameObjects;
@property (strong) NSMutableArray * objectsToRemove;
@property (strong) NSMutableArray * objectsToAdd;


+ (GameController *) sharedGameController;
- (void) tick:(float)dt;
- (void) initializeGame: (ViewController*) viewcontroller;//ERROR: EXPECTED A TYPE

- (void) createObject:(Class) objecttype atPoint:(CGPoint)position;
- (void) deleteObject:(GameObject*) object atPoint:(CGPoint)position;
- (void) manageObjects;

@end

GameController.m

#import "GameController.h"
#import "ViewController.h" // << if you need it in this source
#import "GameObject.h" // << if you need it in this source

@implementation GameController
...

Then you can apply the same treatment to ViewController.h (which is importing GameController.h).