Możliwość umieszczania zmiennych instancji w @implementationbloku lub w rozszerzeniu klasy jest funkcją „nowoczesnego środowiska uruchomieniowego Objective-C”, z którego korzysta każda wersja systemu iOS oraz 64-bitowe programy systemu Mac OS X.
Jeśli chcesz pisać 32-bitowe aplikacje Mac OS X, musisz umieścić zmienne instancji w @interfacedeklaracji. Prawdopodobnie nie musisz jednak obsługiwać 32-bitowej wersji swojej aplikacji. OS X obsługuje aplikacje 64-bitowe od wersji 10.5 (Leopard), która została wydana ponad pięć lat temu.
Załóżmy więc, że piszesz tylko aplikacje, które będą korzystać z nowoczesnego środowiska wykonawczego. Gdzie powinieneś położyć bluszcz?
Opcja 0: w @interface(Nie rób tego)
Najpierw przyjrzyjmy się, dlaczego nie chcemy umieszczać zmiennych instancji w @interfacedeklaracji.
Umieszczenie zmiennych instancji w @interfaceankiecie ujawnia szczegóły implementacji użytkownikom tej klasy. Może to doprowadzić tych użytkowników (nawet Ciebie, gdy używasz własnych klas!) Do polegania na szczegółach implementacji, których nie powinni. (Jest to niezależne od tego, czy deklarujemy ivars @private).
Umieszczenie zmiennych instancji w an @interfacepowoduje, że kompilacja trwa dłużej, ponieważ za każdym razem, gdy dodajemy, zmieniamy lub usuwamy deklarację ivar, musimy rekompilować każdy .mplik, który importuje interfejs.
Dlatego nie chcemy umieszczać zmiennych instancji w @interface. Gdzie powinniśmy je umieścić?
Opcja 2: @implementationbez szelek (nie rób tego)
Następnie omówmy twoją opcję 2, „Umieść iVars pod @implementantion bez bloku nawiasów klamrowych”. To nie deklaruje zmiennych instancji! Mówisz o tym:
@implementation Person
int age;
NSString *name;
...
Ten kod definiuje dwie zmienne globalne. Nie deklaruje żadnych zmiennych instancji.
Dobrze jest zdefiniować zmienne globalne w swoim .mpliku, nawet w swoim @implementation, jeśli potrzebujesz zmiennych globalnych - na przykład, ponieważ chcesz, aby wszystkie Twoje instancje miały wspólny stan, na przykład pamięć podręczną. Ale nie możesz użyć tej opcji do zadeklarowania ivarów, ponieważ nie deklaruje ona ivarów. (Ponadto zmienne globalne prywatne dla implementacji powinny być zwykle deklarowane, staticaby uniknąć zanieczyszczania globalnej przestrzeni nazw i ryzyka błędów czasu łącza).
To pozostawia opcje 1 i 3.
Opcja 1: w @implementationz szelkami (zrób to)
Zwykle chcemy skorzystać z opcji 1: umieść je w swoim głównym @implementationbloku, w nawiasach klamrowych, na przykład:
@implementation Person {
int age;
NSString *name;
}
Umieściliśmy je tutaj, ponieważ utrzymuje to ich istnienie w tajemnicy, zapobiegając problemom, które opisałem wcześniej, i ponieważ zwykle nie ma powodu, aby umieszczać je w rozszerzeniu klasy.
Kiedy więc chcemy użyć Twojej opcji 3, umieszczając je w rozszerzeniu klasy?
Opcja 3: w rozszerzeniu klasy (rób to tylko wtedy, gdy jest to konieczne)
Prawie nigdy nie ma powodu, aby umieszczać je w rozszerzeniu klasy w tym samym pliku, co klasa @implementation. Równie dobrze moglibyśmy po prostu umieścić je @implementationw tym przypadku.
Ale czasami możemy napisać klasę, która jest na tyle duża, że chcemy podzielić jej kod źródłowy na wiele plików. Możemy to zrobić za pomocą kategorii. Na przykład, gdybyśmy implementowali UICollectionView(dość dużą klasę), moglibyśmy zdecydować, że chcemy umieścić kod zarządzający kolejkami widoków wielokrotnego użytku (komórki i widoki uzupełniające) w oddzielnym pliku źródłowym. Moglibyśmy to zrobić, oddzielając te wiadomości do kategorii:
@interface UICollectionView : UIScrollView
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
@end
@interface UICollectionView (ReusableViews)
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
@end
OK, teraz możemy zaimplementować główne UICollectionViewmetody programu UICollectionView.mi możemy zaimplementować metody, które zarządzają widokami wielokrotnego użytku UICollectionView+ReusableViews.m, co sprawia, że nasz kod źródłowy jest nieco łatwiejszy w zarządzaniu.
Ale nasz kod zarządzania widokiem wielokrotnego użytku wymaga pewnych zmiennych instancji. Te zmienne muszą być uwidocznione w głównej klasie @implementationw UICollectionView.m, więc kompilator wyemituje je w .opliku. Musimy również udostępnić te zmienne instancji kodowi w UICollectionView+ReusableViews.m, aby te metody mogły używać funkcji ivars.
W tym miejscu potrzebujemy rozszerzenia klasy. Możemy umieścić moduły Ivars do zarządzania widokiem wielokrotnego użytku w rozszerzeniu klasy w prywatnym pliku nagłówkowym:
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
Nie będziemy wysyłać tego pliku nagłówkowego do użytkowników naszej biblioteki. Po prostu zaimportujemy go do UICollectionView.mi do środka UICollectionView+ReusableViews.m, aby wszystko, co musi zobaczyć te ivars, mogło je zobaczyć. Dodaliśmy również metodę, którą chcemy init, aby wywoływana była metoda main w celu zainicjowania kodu zarządzania widokiem wielokrotnego użytku. Wywołamy tę metodę z -[UICollectionView initWithFrame:collectionViewLayout:]in UICollectionView.mi zaimplementujemy ją w UICollectionView+ReusableViews.m.