Ziel-C: @ class-Direktive vor @interface?

78

Was ist der Unterschied zwischen diesen beiden Klassendeklarationen? Ich verstehe nicht, warum @class hier verwendet wird. Vielen Dank.

@class TestClass;

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

und

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

Antworten:

193

@classexistiert, um zirkuläre Abhängigkeiten zu brechen. Angenommen, Sie haben die Klassen A und B.

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

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

Hähnchen; Ei treffen. Dies kann niemals kompiliert werden, da die Schnittstelle von A davon abhängt, dass B definiert ist und umgekehrt.

Somit kann es behoben werden mit @class:

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

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

@classteilt dem Compiler effektiv mit, dass eine solche Klasse irgendwo existiert, und daher sind Zeiger, die deklariert wurden, um auf Instanzen dieser Klasse zu verweisen, vollkommen gültig. Sie konnten jedoch keine Methode für eine Instanzreferenz aufrufen, deren Typ nur als definiert ist, da @classdem Compiler keine zusätzlichen Metadaten zur Verfügung stehen (ich kann mich nicht erinnern, ob die Aufrufsite wieder als Aufruf durch idoder ausgewertet wird nicht).

In Ihrem Beispiel ist das @classharmlos, aber völlig unnötig.

bbum
quelle
24
Ich wünschte, ich könnte noch eine +1 geben, nur für 'Huhn, treffe Ei'
Swapnil Luktuke
4
Übrigens wird auf die idBewertung zurückgegriffen.
Jheld
Gibt es bestimmte Gründe, warum einige Personen @classihre .h-Datei einfügen und dann die erforderlichen Header-Dateien in ihre .m-Datei importieren, während andere möglicherweise nur die .h-Datei in die aktuelle Klassen-Header-Datei importieren? Nur neugierig, danke. @bbum
Josh Valdivieso
4
@JoshValdivieso Sie werden immer noch schnell zu zirkulären Abhängigkeiten kommen, und das allgemeine Muster besteht darin, die Anzahl der Importe in der Header-Datei zu minimieren, da dies die Kompilierungszeiten erheblich verkürzt (dies ist heutzutage nicht mehr so ​​häufig der Fall vorkompilierte Header).
bbum
18
@class TestClass;

Dies erklärt lediglich "Klasse TestClass wird definiert".

In diesem Fall (dem, den Sie eingefügt haben) hat dies keinerlei Auswirkungen, daher sind diese gleich.

Wenn Sie jedoch ein Protokoll definieren möchten, das Ihren Klassennamen verwendet (z. B. als Parametertyp, der an die Delegierung übergeben wird), müssen Sie dies @class TestClassvor der Protokolldefinition deklarieren , da Ihre Klasse noch nicht definiert ist.

Wenn Sie Ihren Klassennamen angeben müssen, bevor die Klassendefinition erstellt wird, müssen Sie im Allgemeinen @classzuerst eine Deklaration ausstellen

Poncha
quelle
Ich habe diese Implementierung am meisten vor den Protokollen gesehen. danke für das Teilen!
SleepsOnNewspapers
7

Laut Matts Antwort hat die @classErklärung in Ihrem Code absolut keinen Sinn . @classforward definiert eine Klasse, damit der Compiler anschließend weiß, auf welche allgemeine Art von Einheit Sie sich beziehen. Da Objective-C zur Laufzeit so gut wie typenlos ist, ist dies oft alles, was der Compiler tatsächlich wissen muss - gerade genug, um das Ding von einem atomaren C-Wert zu unterscheiden.

Ich werde einen Stich in die Dunkelheit machen und sagen, dass, weil die Instanzvariablen in der deklariert sind, @interfaceSie einen alten Code betrachten. Da es sich um alten Code handelt, befand sich der Code @classwahrscheinlich früher an einem anderen Ort (z. B. wurde dazwischen ein Delegatenprotokoll deklariert) und ist gerade harmlos gestrandet.

Tommy
quelle
4

@class ist sehr praktisch, wenn Sie ein Protokoll für ein Objekt definieren müssen, das normalerweise mit dem Objekt interagiert, dessen Schnittstelle Sie ebenfalls definieren. Mit @class können Sie die Protokolldefinition im Header Ihrer Klasse behalten. Dieses Delegierungsmuster wird häufig in Objective-C verwendet und ist häufig der Definition von "MyClass.h" und "MyClassDelegate.h" vorzuziehen. Dies kann zu verwirrenden Importproblemen führen

@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

Wenn Sie dann die Klasse verwenden, können Sie sowohl Instanzen der Klasse erstellen als auch das Protokoll mit einer einzigen Importanweisung implementieren

#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];

}
vsanthanam510
quelle