Książka „iOS6 by Tutorials” autorstwa Raya Wenderlicha zawiera bardzo fajny rozdział o pisaniu bardziej „nowoczesnego” kodu Objective-C. W jednej sekcji książki opisują, jak przenieść iVars z nagłówka klasy do pliku implementacji. Ponieważ wszystkie iVary powinny być prywatne, wydaje się to słuszne.
Ale do tej pory znalazłem 3 sposoby na zrobienie tego. Każdy robi to inaczej.
1.) Umieść iVars pod @implementantion wewnątrz bloku nawiasów klamrowych (tak jest to zrobione w książce).
2.) Umieść iVars pod @implementantion bez bloku nawiasów klamrowych
3.) Umieść iVars wewnątrz prywatnego interfejsu powyżej @implementantion (rozszerzenie klasy)
Wszystkie te rozwiązania wydają się działać dobrze i do tej pory nie zauważyłem żadnej różnicy w zachowaniu mojej aplikacji. Wydaje mi się, że nie ma „właściwego” sposobu na zrobienie tego, ale muszę napisać kilka tutoriali i wybrać tylko jeden sposób dla mojego kodu.
Którędy mam iść?
Edycja: mówię tutaj tylko o iVars. Nie właściwości. Tylko dodatkowe zmienne, których obiekt potrzebuje tylko dla siebie i które nie powinny być wystawiane na zewnątrz.
Przykłady kodu
1)
#import "Person.h"
@implementation Person
{
int age;
NSString *name;
}
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
2)
#import "Person.h"
@implementation Person
int age;
NSString *name;
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
3)
#import "Person.h"
@interface Person()
{
int age;
NSString *name;
}
@end
@implementation Person
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
źródło
Odpowiedzi:
Możliwość umieszczania zmiennych instancji w
@implementation
bloku 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
@interface
deklaracji. 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
@interface
deklaracji.Umieszczenie zmiennych instancji w
@interface
ankiecie 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
@interface
powoduje, że kompilacja trwa dłużej, ponieważ za każdym razem, gdy dodajemy, zmieniamy lub usuwamy deklarację ivar, musimy rekompilować każdy.m
plik, który importuje interfejs.Dlatego nie chcemy umieszczać zmiennych instancji w
@interface
. Gdzie powinniśmy je umieścić?Opcja 2:
@implementation
bez 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
.m
pliku, 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,static
aby uniknąć zanieczyszczania globalnej przestrzeni nazw i ryzyka błędów czasu łącza).To pozostawia opcje 1 i 3.
Opcja 1: w
@implementation
z szelkami (zrób to)Zwykle chcemy skorzystać z opcji 1: umieść je w swoim głównym
@implementation
bloku, 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@implementation
w 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:// UICollectionView.h @interface UICollectionView : UIScrollView - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; @property (nonatomic, retain) UICollectionView *collectionViewLayout; // etc. @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
UICollectionView
metody programuUICollectionView.m
i możemy zaimplementować metody, które zarządzają widokami wielokrotnego użytkuUICollectionView+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
@implementation
wUICollectionView.m
, więc kompilator wyemituje je w.o
pliku. Musimy również udostępnić te zmienne instancji kodowi wUICollectionView+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:
// UICollectionView_ReusableViewsSupport.h @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.m
i do środkaUICollectionView+ReusableViews.m
, aby wszystko, co musi zobaczyć te ivars, mogło je zobaczyć. Dodaliśmy również metodę, którą chcemyinit
, 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:]
inUICollectionView.m
i zaimplementujemy ją wUICollectionView+ReusableViews.m
.źródło
readonly
właściwość jako prywatnąreadwrite
, musisz użyć rozszerzenia klasy. Ale pytanie dotyczyło tego, gdzie umieścić blaszki, a nie gdzie umieścić oświadczenia majątkowe. Nie widzę, jak użycie rozszerzenia klasy może uniemożliwić implementacji dostęp do ivars. Aby to zrobić, należałoby umieścić implementacje metod w kategorii, ponieważ kompilator musi zobaczyć wszystkie rozszerzenia klas deklarujące ivar, zanim zobaczy rozszerzenie@implementation
.Opcja 2 jest całkowicie błędna. Są to zmienne globalne, a nie zmienne instancji.
Opcje 1 i 3 są zasadniczo identyczne. To nie ma żadnego znaczenia.
Wybór polega na tym, czy umieścić zmienne instancji w pliku nagłówkowym, czy w pliku implementacji. Zaletą korzystania z pliku nagłówkowego jest to, że masz szybki i łatwy skrót klawiaturowy (Command + Control + Up w Xcode) do przeglądania i edytowania zmiennych instancji oraz deklaracji interfejsu.
Wadą jest to, że ujawniasz prywatne szczegóły swojej klasy w publicznym nagłówku. W niektórych przypadkach jest to niepożądane, szczególnie jeśli piszesz kod do użytku innych. Innym potencjalnym problemem jest to, że jeśli używasz Objective-C ++, dobrze jest unikać umieszczania jakichkolwiek typów danych C ++ w pliku nagłówkowym.
Zmienne instancji implementacji są świetną opcją w niektórych sytuacjach, ale dla większości mojego kodu nadal umieszczam zmienne instancji w nagłówku tylko dlatego, że jest to wygodniejsze dla mnie jako programisty pracującego w Xcode. Radzę robić to, co uważasz za wygodniejsze.
źródło
W dużej mierze ma to związek z widocznością ivar dla podklas. Podklasy nie będą miały dostępu do zmiennych instancji zdefiniowanych w
@implementation
bloku.W przypadku kodu wielokrotnego użytku, który planuję rozpowszechniać (np. Kod biblioteki lub frameworka), w którym wolę nie ujawniać zmiennych instancji do publicznego wglądu, skłaniam się do umieszczania ivarów w bloku implementacji (opcja 1).
źródło
Należy umieścić zmienne instancji w prywatnym interfejsie powyżej implementacji. Wariant 3.
Dokumentacją do przeczytania na ten temat jest przewodnik Programming in Objective-C .
Z dokumentacji:
źródło
Publiczne ivary powinny być naprawdę zadeklarowane jako właściwości w @interface (prawdopodobnie to, o czym myślisz w 1). Prywatne ivars, jeśli korzystasz z najnowszego Xcode i nowoczesnego środowiska uruchomieniowego (64-bitowy system OS X lub iOS), można zadeklarować w @implementation (2), a nie w rozszerzeniu klasy, co prawdopodobnie jest tym, co ' myślę o w 3.
źródło