Rozumiem, że należy użyć deklaracji klasy forward w przypadku, gdy ClassA musi zawierać nagłówek ClassB, a ClassB musi zawierać nagłówek ClassA, aby uniknąć okrągłych inkluzji. Rozumiem również, że an #import
jest proste, ifndef
więc dołączenie zdarza się tylko raz.
Moje pytanie brzmi: kiedy się używa, #import
a kiedy się używa @class
? Czasami jeśli używam @class
deklaracji, widzę wspólne ostrzeżenie kompilatora, takie jak następujące:
warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.
Naprawdę chciałbym to zrozumieć, zamiast po prostu usunąć @class
deklarację przesyłania dalej i wrzucić #import
w celu wyciszenia ostrzeżeń, które daje mi kompilator.
objective-c
cocoa
cocoa-touch
Coocoo4Cocoa
źródło
źródło
Odpowiedzi:
Jeśli zobaczysz to ostrzeżenie:
potrzebujesz
#import
tego pliku, ale możesz to zrobić w pliku implementacji (.m) i użyć@class
deklaracji w pliku nagłówkowym.@class
nie usuwa (zwykle) potrzeby#import
plików, po prostu przenosi wymaganie w dół do miejsca, w którym informacje są przydatne.Na przykład
Jeśli powiesz
@class MyCoolClass
, kompilator wie, że może zobaczyć coś takiego:Nie musi się martwić o nic innego niż
MyCoolClass
poprawną klasę i powinien zarezerwować miejsce na wskaźnik do niej (tak naprawdę tylko wskaźnik). Zatem w nagłówku@class
wystarcza 90% czasu.Jednak jeśli kiedykolwiek będziesz chciał utworzyć
myObject
członków lub uzyskać do nich dostęp , musisz poinformować kompilator o tym, jakie są te metody. W tym momencie (przypuszczalnie w pliku implementacyjnym) będziesz musiał#import "MyCoolClass.h"
przekazać kompilatorowi dodatkowe informacje poza „to jest klasa”.źródło
@class
coś w.h
pliku, ale zapomnij o#import
nim w .m, próby uzyskania dostępu do metody na@class
obiekcie ED, i dostać ostrzeżenia jak:warning: no -X method found
.Trzy proste zasady:
#import
superklasa i przyjęte protokoły w plikach nagłówkowych (.h
plikach).#import
wszystkie klasy i protokoły, do których wysyłane są wiadomości w implementacji (.m
pliki).Jeśli przekażesz deklarację w plikach implementacyjnych, prawdopodobnie zrobisz coś złego.
źródło
Zobacz dokumentację języka programowania Objective-C w ADC
W sekcji dotyczącej definiowania klasy | Interfejs klasy opisuje, dlaczego tak się dzieje:
Mam nadzieję, że to pomoże.
źródło
W razie potrzeby użyj deklaracji przekazywania w pliku nagłówkowym oraz
#import
plików nagłówkowych dla wszystkich klas używanych w implementacji. Innymi słowy, zawsze używasz#import
plików, których używasz w swojej implementacji, a jeśli potrzebujesz odwoływać się do klasy w pliku nagłówkowym, użyj również deklaracji przesyłania dalej.Wyjątkiem jest to, że należy
#import
klasą lub formalny protokół jesteś dziedziczenie z nagłówka w pliku (w takim przypadku nie będzie musiała importować go w realizacji).źródło
Powszechną praktyką jest używanie @class w plikach nagłówkowych (ale nadal trzeba #importować nadklasę) i #import w plikach implementacyjnych. Pozwoli to uniknąć okrągłych wtrąceń i po prostu działa.
źródło
#import
„jest jak dyrektywy #include C, z wyjątkiem, że to sprawia, że na pewno, że ten sam plik nie jest zawarty więcej niż jeden raz.” Tak więc zgodnie z tym#import
dba się o inkluzje kołowe,@class
dyrektywy nie pomagają w tym szczególnie.Kolejna zaleta: Szybka kompilacja
Jeśli dołączasz plik nagłówka, każda zmiana spowoduje, że bieżący plik również się skompiluje, ale nie dzieje się tak, jeśli nazwa klasy jest uwzględniona jako
@class name
. Oczywiście musisz dołączyć nagłówek do pliku źródłowegoźródło
Prosta odpowiedź: Ty
#import
lub#include
kiedy istnieje fizyczna zależność. W przeciwnym razie, należy użyć do przodu deklaracji (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Oto kilka typowych przykładów fizycznej zależności:
CGPoint
jako ivar lub właściwość, kompilator będzie musiał zobaczyć deklaracjęCGPoint
.Kompilator jest pod tym względem bardzo łagodny. Będzie upuszczał podpowiedzi (takie jak powyższe), ale możesz łatwo wyrzucić stos, jeśli je zignorujesz i nie zrobisz tego
#import
poprawnie. Chociaż powinien (IMO), kompilator tego nie wymusza. W ARC kompilator jest bardziej rygorystyczny, ponieważ odpowiada za zliczanie referencji. Dzieje się tak, gdy kompilator wraca do stanu domyślnego, gdy napotyka nieznaną metodę, którą wywołujesz. Przyjmowana jest każda zwracana wartość i parametrid
. Dlatego należy usunąć każde ostrzeżenie z baz kodu, ponieważ należy to uznać za zależność fizyczną. Jest to analogiczne do wywoływania funkcji C, która nie jest zadeklarowana. W przypadku C zakłada się, że parametry sąint
.Powodem, dla którego preferujesz deklaracje forward, jest to, że możesz skrócić czas kompilacji przez czynniki, ponieważ istnieje minimalna zależność. W przypadku deklaracji przesyłania dalej kompilator widzi nazwę i może poprawnie parsować i kompilować program bez wyświetlania deklaracji klasy lub wszystkich jej zależności, gdy nie ma zależności fizycznej. Czyste wersje zajmują mniej czasu. Kompilacje przyrostowe zajmują mniej czasu. Oczywiście skończysz trochę dłużej, upewniając się, że wszystkie potrzebne nagłówki są widoczne w każdym tłumaczeniu, ale szybko się to zwraca (przy założeniu, że twój projekt nie jest mały).
Jeśli używasz
#import
lub#include
zamiast tego, kompilujesz dużo więcej pracy niż jest to konieczne. Wprowadzasz również złożone zależności nagłówka. Możesz to porównać do algorytmu brutalnej siły. Podczas#import
przeciągania ton niepotrzebnych informacji, które wymagają dużej ilości pamięci, dyskowych operacji we / wy i procesora do przeanalizowania i skompilowania źródeł.ObjC jest bardzo zbliżony do ideału dla języka opartego na języku C, jeśli chodzi o zależność, ponieważ
NSObject
typy nigdy nie są wartościami -NSObject
typy są zawsze wskaźnikowymi licznikami odniesienia. Dzięki temu możesz uniknąć niesamowicie szybkich czasów kompilacji, jeśli odpowiednio ustrukturyzujesz zależności programu i przekażesz je tam, gdzie to możliwe, ponieważ wymagana jest bardzo niewielka zależność fizyczna. Możesz także zadeklarować właściwości w rozszerzeniach klas, aby dodatkowo zminimalizować zależność. To ogromny bonus dla dużych systemów - znałbyś różnicę, gdybyś kiedykolwiek opracował dużą bazę kodu C ++.Dlatego zalecam, aby w miarę możliwości używać naprzód, a następnie
#import
tam, gdzie występuje fizyczna zależność. Jeśli zobaczysz ostrzeżenie lub inne wskazujące na fizyczną zależność - napraw je wszystkie. Poprawka znajduje się#import
w pliku implementacji.Podczas budowania bibliotek prawdopodobnie sklasyfikujesz niektóre interfejsy jako grupę, w którym to przypadku zrobiłbyś
#import
bibliotekę, w której wprowadzono zależność fizyczną (np#import <AppKit/AppKit.h>
.). Może to wprowadzić zależność, ale opiekunowie bibliotek często potrafią obsłużyć fizyczne zależności w zależności od potrzeb - jeśli wprowadzą funkcję, mogą zminimalizować wpływ, jaki ma ona na twoje kompilacje.źródło
NSObject types are never values -- NSObject types are always reference counted pointers.
nie do końca prawda. Bloki rzucają lukę w twojej odpowiedzi, mówiąc tylko.Widzę dużo „Zrób to w ten sposób”, ale nie widzę żadnych odpowiedzi na „Dlaczego?”
Więc: dlaczego powinieneś @class w nagłówku i #import tylko w swojej implementacji? Podwajasz swoją pracę, cały czas @class i #import. Chyba że skorzystasz z dziedziczenia. W takim przypadku będziesz #importować wiele razy dla jednej @ klasy. Następnie musisz pamiętać o usunięciu z wielu różnych plików, jeśli nagle zdecydujesz, że nie potrzebujesz już dostępu do deklaracji.
Wielokrotne importowanie tego samego pliku nie stanowi problemu ze względu na naturę #import. Kompilacja wydajności nie jest tak naprawdę problemem. Gdyby tak było, nie byłoby #importowania Cocoa / Cocoa.h lub podobnego w prawie każdym pliku nagłówkowym.
źródło
jeśli to zrobimy
oznacza to, że dziedziczymy Class_A do Class_B, w Class_B możemy uzyskać dostęp do wszystkich zmiennych klasy_A.
jeśli to robimy
tutaj mówimy, że używamy Class_A w naszym programie, ale jeśli chcemy użyć zmiennych Class_A w Class_B, musimy #importować Class_A w pliku .m (stworzyć obiekt i użyć jego funkcji i zmiennych).
źródło
Aby uzyskać dodatkowe informacje o zależnościach plików oraz #import i @class, sprawdź to:
http://qualitycoding.org/file-dependencies/ to dobry artykuł
streszczenie artykułu
źródło
Kiedy się rozwijam, mam na myśli tylko trzy rzeczy, które nigdy nie powodują żadnych problemów.
Dla wszystkich innych klas (podklas i klas potomnych w moim projekcie self), deklaruję je poprzez klasę forward.
źródło
Jeśli spróbujesz zadeklarować zmienną lub właściwość w pliku nagłówkowym, którego jeszcze nie zaimportowałeś, pojawi się błąd informujący, że kompilator nie zna tej klasy.
Prawdopodobnie twoja pierwsza myśl
#import
.W niektórych przypadkach może to powodować problemy.
Na przykład, jeśli zaimplementujesz kilka metod C w pliku nagłówkowym, strukturach lub czegoś podobnego, ponieważ nie należy ich importować wiele razy.
Dlatego możesz powiedzieć kompilatorowi
@class
:Zasadniczo mówi kompilatorowi, aby zamknął się i skompilował, nawet jeśli nie jest pewne, czy ta klasa kiedykolwiek zostanie zaimplementowana.
Zazwyczaj użyć
#import
w .m i@class
w .h plików.źródło
Przekaż deklarację tylko po to, aby kompilator nie wyświetlał błędu.
kompilator będzie wiedział, że istnieje klasa o nazwie użytej w pliku nagłówkowym do zadeklarowania.
źródło
Kompilator będzie narzekał tylko, jeśli zamierzasz używać tej klasy w taki sposób, że kompilator musi znać jej implementację.
Dawny:
Nie będzie narzekać, jeśli zamierzasz użyć go jako wskaźnika. Oczywiście będziesz musiał #importować go do pliku implementacji (jeśli tworzysz instancję obiektu tej klasy), ponieważ musi znać zawartość klasy, aby utworzyć instancję obiektu.
UWAGA: #import to nie to samo co #include. Oznacza to, że nie ma nic takiego jak import cykliczny. import jest rodzajem żądania kompilatora, aby szukał określonego pliku w celu uzyskania pewnych informacji. Jeśli te informacje są już dostępne, kompilator je ignoruje.
Po prostu spróbuj, zaimportuj Ah w Bh i Bh w Ah. Nie będzie żadnych problemów ani skarg, a także zadziała.
Kiedy używać @class
Korzystasz z @class tylko wtedy, gdy nie chcesz nawet importować nagłówka do nagłówka. Może to być przypadek, w którym nawet nie chcesz wiedzieć, czym będzie ta klasa. Przypadki, w których możesz nawet nie mieć nagłówka dla tej klasy.
Przykładem może być pisanie dwóch bibliotek. Jedna klasa, nazwijmy ją A, istnieje w jednej bibliotece. Ta biblioteka zawiera nagłówek z drugiej biblioteki. Ten nagłówek może mieć wskaźnik A, ale znowu może nie być konieczne jego użycie. Jeśli biblioteka 1 nie jest jeszcze dostępna, biblioteka B nie zostanie zablokowana, jeśli użyjesz @class. Ale jeśli chcesz zaimportować Ah, postępy biblioteki 2 są zablokowane.
źródło
Pomyśl o @class jako mówieniu kompilatorowi: „zaufaj mi, to istnieje”.
Pomyśl o #import jak o kopiowaniu i wklejaniu.
Chcesz zminimalizować liczbę importów, które masz z wielu powodów. Bez badań pierwszą rzeczą, która przychodzi na myśl, jest to, że skraca czas kompilacji.
Zauważ, że kiedy dziedziczysz po klasie, nie możesz po prostu użyć deklaracji forward. Musisz zaimportować plik, aby deklarowana klasa wiedziała, jak jest zdefiniowany.
źródło
To przykładowy scenariusz, w którym potrzebujemy @class.
Zastanów się, czy chcesz utworzyć protokół w pliku nagłówkowym, który ma parametr z typem danych tej samej klasy, możesz użyć @class. Pamiętaj, że możesz również deklarować protokoły osobno, to tylko przykład.
źródło