Завдання-C: Директива @class перед @interface?


79

У чому різниця між цими двома оголошеннями класу? Я не розумію, чому тут використовується @class. Дякую.

@class TestClass;

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

і

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

Відповіді:


195

@classіснує для розриву кругових залежностей. Скажімо, у вас є класи A і B.

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

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

Курка; зустріти Яйце. Це ніколи не може скомпілюватися, оскільки інтерфейс A залежить від того, як B визначається, і навпаки.

Таким чином, це можна виправити, використовуючи @class:

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

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

@classфактично повідомляє компілятору, що такий клас десь існує, і, таким чином, покажчики, оголошені, що вказують на екземпляри згаданого класу, є цілком дійсними. Однак ви не можете викликати метод у посиланні на екземпляр, тип якого визначений лише як, @classоскільки додаткові метадані не доступні компілятору (я не пам'ятаю, чи повертає сайт виклику для оцінки як виклик через idабо ні).

У вашому прикладі @classце нешкідливо, але зовсім непотрібно.


25
я хотів би дати ще +1 лише за "курку, зустріти яйце"
Swapnil Luktuke

4
До речі, він повертається до idоцінки.
jheld

Чи існують конкретні причини, чому деякі особи вставляють @classсвій файл .h, а потім імпортують необхідні файли заголовків у свій файл .m, а інші можуть просто імпортувати файл .h у поточний файл заголовка класів? Просто цікаво, дякую. @bbum
Джош Вальдівієсо

4
@JoshValdivieso Ви все одно швидко отримаєте кругові залежності, і загальний шаблон полягає в тому, щоб мінімізувати кількість імпортів у файлі заголовка, як це використовувалося для значного скорочення часу компіляції (це не так вже й складно в цей день та вік попередньо скомпільовані заголовки).
bbum

18
@class TestClass;

Це просто декларує "клас TestClass буде визначений".

У цьому випадку (того, який ви вставили) це не має жодного ефекту, тому це однаково.

Однак, якщо ви збираєтеся визначити протокол, який буде використовувати ім’я вашого класу (наприклад, як тип параметрів, переданих для делегування), вам потрібно буде оголосити це @class TestClassперед визначенням протоколу, оскільки ваш клас все ще не визначений.

Загалом, якщо вам потрібно згадати назву вашого класу до того, як буде зроблено визначення класу, @classспочатку вам потрібно буде оформити декларацію


я бачив цю реалізацію найбільше - до протоколів. Дякую, що поділились!
SleepsOnNewspapers

7

Відповідно до відповіді Метта, @classу вашому коді немає абсолютно жодного сенсу до декларації.@classforward визначає клас, щоб згодом компілятор знав, до якого загального типу одиниці ви маєте на увазі. Оскільки Objective-C майже нетиповий під час виконання, це часто все, що насправді потрібно знати компілятору - достатньо, щоб відрізнити річ від атомного значення C.

Я збираюся вдарити в темряві і сказати, що, оскільки змінні екземпляра оголошені в тому, @interfaceщо ви дивитесь на старий код. Оскільки це старий код, @classймовірно, він колись був десь в іншому місці (наприклад, між ними був оголошений протокол делегатів) і щойно опинився нешкідливо.


4

@class досить зручний, коли вам потрібно визначити протокол для об'єкта, який зазвичай взаємодіє з об'єктом, інтерфейс якого ви також визначаєте. Використовуючи @class, ви можете зберегти визначення протоколу в заголовку вашого класу. Цей шаблон делегування часто використовується в Objective-C, і часто кращий, ніж визначення "MyClass.h" і "MyClassDelegate.h". Це може спричинити незрозумілі проблеми з імпортом

@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

Потім, коли ви використовуєте клас, ви можете як створювати екземпляри класу, так і реалізовувати протокол за допомогою одного оператора імпорту

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.