Objective-C: @class Directive before @interface?

30,564

Solution 1

@class exists to break circular dependencies. Say you have classes A and B.

@interface A:NSObject
- (B*)calculateMyBNess;
@end

@interface B:NSObject
- (A*)calculateMyANess;
@end

Chicken; meet Egg. This can never compile because A's interface depends on B being defined and vice-versa.

Thus, it can be fixed by using @class:

@class B;
@interface A:NSObject
- (B*)calculateMyBNess;
@end

@interface B:NSObject
- (A*)calculateMyANess;
@end

@class effectively tells the compiler that such a class exists somewhere and, thus, pointers declared to point to instances of said class are perfectly valid. However, you couldn't call a method on an instance reference whose type is only defined as an @class because there is no additional metadata available to the compiler (I can't remember if it reverts the call site to being evaluated as a call through id or not).

In your example, the @class is harmless, but entirely unnecessary.

Solution 2

@class TestClass;

This merely declares "class TestClass will be defined".

In this case (the one you pasted) this has no effect of whatsoever, so these are the same.

However, in case you're going to define a protocol that would use your class name (as type of parameters passed to delegate, for example), you will need to declare @class TestClass before the protocol definition, as your class is still not defined.

In general, if you need to mention your class name before the class definition is made, you will need to issue @class declaration first

Solution 3

As per Matt's answer there's absolutely no point to the @class declaration in your code. @class forward defines a class so that the compiler subsequently knows what general sort of unit you are referring to. Since Objective-C is pretty much typeless at runtime, that's often all that the compiler actually needs to know — just enough to distinguish the thing from an atomic C value.

I'm going to take a stab in the dark and say that because the instance variables are declared in the @interface you're looking at some old code. Because it's old code, the @class probably used to be somewhere else (eg, there was a delegate protocol declared in between) and has just ended up harmlessly stranded.

Solution 4

@class is pretty handy when you need to define a protocol for an object that will usually interact with the object whose interface you are also defining. Using @class, you can keep the protocol definition in header of your class. This pattern of delegation is often used on Objective-C, and is often preferable to defining both "MyClass.h" and "MyClassDelegate.h". That can cause some confusing import problems

@class MyClass;

@protocol MyClassDelegate<NSObject>

- (void)myClassDidSomething:(MyClass *)myClass
- (void)myClass:(MyClass *)myClass didSomethingWithResponse:(NSObject *)reponse
- (BOOL)shouldMyClassDoSomething:(MyClass *)myClass;
- (BOOL)shouldMyClass:(MyClass *)myClass doSomethingWithInput:(NSObject *)input

@end

// MyClass hasn't been defined yet, but MyClassDelegate will still compile even tho
// params mention MyClass, because of the @class declaration.
// You're telling the compiler "it's coming. don't worry".
// You can't send MyClass any messages (you can't send messages in a protocol declaration anyway),
// but it's important to note that @class only lets you reference the yet-to-be-defined class. That's all.
// The compiler doesn't know anything about MyClass other than its definition is coming eventually.

@interface MyClass : NSObject

@property (nonatomic, assign) id<MyClassDelegate> delegate;

- (void)doSomething;
- (void)doSomethingWithInput:(NSObject *)input

@end

Then, when you are using the class, you can both create instances of the class as well as implement the protocol with a single import statement

#import "MyClass.h"

@interface MyOtherClass()<MyClassDelegate>

@property (nonatomic, strong) MyClass *myClass;

@end

@implementation MyOtherClass

#pragma mark - MyClassDelegate Protocol Methods

- (void)myClassDidSomething:(MyClass *)myClass {

    NSLog(@"My Class Did Something!")

}

- (void)myClassDidSomethingWithResponse:(NSObject *)response {

    NSLog(@"My Class Did Something With %@", response);

}

- (BOOL)shouldMyClassDoSomething {

    return YES;

- (BOOL)shouldMyClassDoSomethingWithInput:(NSObject *)input {

    if ([input isEqual:@YES]) {

        return YES;

    }

    return NO;

}


- (void)doSomething {

    self.myClass = [[MyClass alloc] init];
    self.myClass.delegate = self;
    [self.myClass doSomething];
    [self.myClass doSomethingWithInput:@0];

}
Share:
30,564
Ryan
Author by

Ryan

Updated on January 05, 2020

Comments

  • Ryan
    Ryan over 4 years

    What is the difference between these two class declarations? I don't understand why @class is utilized here. Thanks.

    @class TestClass;
    
    @interface TestClass: UIView {
        UIImage *image1;
        UIImage *image2;
    }
    

    and

    @interface TestClass: UIView {
        UIImage *image1;
        UIImage *image2;
    }
    
  • Swapnil Luktuke
    Swapnil Luktuke almost 11 years
    i wish i could give another +1 just for 'chicken, meet egg'
  • jheld
    jheld almost 10 years
    BTW, it does revert to the id evaluation.
  • Josh Valdivieso
    Josh Valdivieso almost 10 years
    Are there particular reasons why some individuals put @class in their .h file and then import the necessary header files in their .m file, and others might just import the .h file in the current classes header file? Just curious, thanks. @bbum
  • bbum
    bbum almost 10 years
    @JoshValdivieso You'll still quickly end up with circular dependencies and the general pattern is to minimize the # of imports in the header file as it used to significantly reduce compile times (that isn't so much the case in this day and age of precompiled headers).
  • SleepsOnNewspapers
    SleepsOnNewspapers about 9 years
    ive seen this implementation the most- before the protocols. thanks for sharing!