Das Buch "iOS6 by Tutorials" von Ray Wenderlich enthält ein sehr schönes Kapitel über das Schreiben von "modernerem" Objective-C-Code. In einem Abschnitt wird beschrieben, wie iVars aus dem Header der Klasse in die Implementierungsdatei verschoben werden. Da alle iVars privat sein sollten, scheint dies das Richtige zu sein.
Bisher habe ich jedoch drei Möglichkeiten gefunden. Jeder macht es anders.
1.) Setzen Sie iVars unter @implementantion in einen Block geschweifter Klammern (so wird es im Buch gemacht).
2.) Setzen Sie iVars unter @implementantion ohne Block geschweifter Klammern
3.) Platzieren Sie iVars in der privaten Schnittstelle über dem @implementantion (eine Klassenerweiterung).
Alle diese Lösungen scheinen gut zu funktionieren, und bisher habe ich keinen Unterschied im Verhalten meiner Anwendung festgestellt. Ich denke, es gibt keinen "richtigen" Weg, dies zu tun, aber ich muss einige Tutorials schreiben und möchte nur einen Weg für meinen Code wählen.
Welchen Weg soll ich gehen?
Edit: Ich spreche hier nur von iVars. Keine Eigenschaften. Nur zusätzliche Variablen, die das Objekt nur für sich selbst benötigt und die nicht nach außen exponiert werden sollten.
Codebeispiele
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
quelle
Antworten:
Die Möglichkeit, Instanzvariablen in den
@implementation
Block oder in eine Klassenerweiterung einzufügen, ist eine Funktion der „modernen Objective-C-Laufzeit“, die von jeder iOS-Version und von 64-Bit-Mac OS X-Programmen verwendet wird.Wenn Sie 32-Bit-Mac OS X-Apps schreiben möchten, müssen Sie Ihre Instanzvariablen in die
@interface
Deklaration einfügen. Möglicherweise müssen Sie jedoch keine 32-Bit-Version Ihrer App unterstützen. OS X unterstützt seit Version 10.5 (Leopard), die vor über fünf Jahren veröffentlicht wurde, 64-Bit-Apps.Nehmen wir also an, Sie schreiben nur Apps, die die moderne Laufzeit verwenden. Wo solltest du deine Ivars hinstellen?
Option 0: In der
@interface
(Don't Do It)Lassen Sie uns zunächst untersuchen, warum wir keine Instanzvariablen in eine
@interface
Deklaration einfügen möchten .Durch das Einfügen von Instanzvariablen in wird
@interface
den Benutzern der Klasse Details der Implementierung angezeigt. Dies kann dazu führen, dass sich diese Benutzer (auch Sie selbst, wenn Sie Ihre eigenen Klassen verwenden!) Auf Implementierungsdetails verlassen, die sie nicht sollten. (Dies ist unabhängig davon, ob wir die Ivare deklarieren@private
.)Wenn Sie Instanzvariablen in eine Datei
@interface
einfügen, dauert das Kompilieren länger, da wir jedes Mal, wenn wir eine ivar-Deklaration hinzufügen, ändern oder entfernen, jede.m
Datei neu kompilieren müssen , die die Schnittstelle importiert.Wir wollen also keine Instanzvariablen in die
@interface
. Wo sollen wir sie hinstellen?Option 2: In den
@implementation
ohne Klammern (Don't Do It)Lassen Sie uns als Nächstes Ihre Option 2 diskutieren: "Setzen Sie iVars unter @implementantion ohne Block geschweifter Klammern". Dies deklariert keine Instanzvariablen! Sie sprechen darüber:
@implementation Person int age; NSString *name; ...
Dieser Code definiert zwei globale Variablen. Es werden keine Instanzvariablen deklariert.
Es ist in Ordnung, globale Variablen in Ihrer
.m
Datei zu definieren , auch in Ihrer@implementation
, wenn Sie globale Variablen benötigen - zum Beispiel, weil alle Ihre Instanzen einen bestimmten Status haben sollen, z. B. einen Cache. Sie können diese Option jedoch nicht zum Deklarieren von Ivars verwenden, da Ivars nicht deklariert werden. (Außerdem sollten globale Variablen, die für Ihre Implementierung privat sind, normalerweise deklariert werdenstatic
, um eine Verschmutzung des globalen Namespace und das Risiko von Verbindungszeitfehlern zu vermeiden.)Damit bleiben Ihre Optionen 1 und 3.
Option 1: In den
@implementation
mit Klammern (Do It)Normalerweise möchten wir Option 1 verwenden: Setzen Sie sie
@implementation
in geschweiften Klammern wie folgt in Ihren Hauptblock:@implementation Person { int age; NSString *name; }
Wir setzen sie hier ein, weil es ihre Existenz privat hält und die zuvor beschriebenen Probleme verhindert, und weil es normalerweise keinen Grund gibt, sie in eine Klassenerweiterung aufzunehmen.
Wann möchten wir Ihre Option 3 verwenden und sie in eine Klassenerweiterung einfügen?
Option 3: In einer Klassenerweiterung (nur bei Bedarf ausführen)
Es gibt fast nie einen Grund, sie in einer Klassenerweiterung in derselben Datei wie die Klasse abzulegen
@implementation
. Wir könnten sie@implementation
in diesem Fall genauso gut in den Fall setzen.Gelegentlich schreiben wir jedoch eine Klasse, die groß genug ist, um den Quellcode in mehrere Dateien aufzuteilen. Wir können das mit Kategorien machen. Wenn wir beispielsweise implementieren
UICollectionView
(eine ziemlich große Klasse), könnten wir entscheiden, dass wir den Code, der die Warteschlangen wiederverwendbarer Ansichten (Zellen und zusätzliche Ansichten) verwaltet, in eine separate Quelldatei einfügen möchten. Wir könnten das tun, indem wir diese Nachrichten in eine Kategorie aufteilen:// 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, jetzt können wir die
UICollectionView
Hauptmethoden inUICollectionView.m
implementieren und die Methoden implementierenUICollectionView+ReusableViews.m
, mit denen wiederverwendbare Ansichten verwaltet werden , wodurch unser Quellcode ein wenig übersichtlicher wird.Unser wiederverwendbarer Ansichtsverwaltungscode benötigt jedoch einige Instanzvariablen. Diese Variablen müssen für die Hauptklasse
@implementation
inUICollectionView.m
verfügbar gemacht werden, damit der Compiler sie in der.o
Datei ausgibt. Außerdem müssen wir diese Instanzvariablen für den Code in verfügbar machenUICollectionView+ReusableViews.m
, damit diese Methoden die ivars verwenden können.Hier brauchen wir eine Klassenerweiterung. Wir können die ivars für die Verwaltung wiederverwendbarer Ansichten in eine Klassenerweiterung in einer privaten Header-Datei einfügen:
// UICollectionView_ReusableViewsSupport.h @interface UICollectionView () { NSMutableDictionary *registeredCellSources; NSMutableDictionary *spareCellsByIdentifier; NSMutableDictionary *registeredSupplementaryViewSources; NSMutableDictionary *spareSupplementaryViewsByIdentifier; } - (void)initReusableViewSupport; @end
Wir werden diese Header-Datei nicht an Benutzer unserer Bibliothek senden. Wir importieren nur in
UICollectionView.m
und inUICollectionView+ReusableViews.m
, so dass alles, was Bedürfnisse diese ivars zu sehen , sie sehen. Wir haben auch eine Methode eingeführt, die die Hauptmethodeinit
aufrufen soll, um den Code für die Verwaltung wiederverwendbarer Ansichten zu initialisieren. Wir werden diese Methode von-[UICollectionView initWithFrame:collectionViewLayout:]
in aufrufenUICollectionView.m
und in implementierenUICollectionView+ReusableViews.m
.quelle
readonly
Eigenschaft überschreiben möchten, um privat zu seinreadwrite
, müssen Sie eine Klassenerweiterung verwenden. Aber die Frage war, wo man Ivars platzieren sollte, nicht wo man Eigentumserklärungen platzieren sollte. Ich sehe nicht, wie die Verwendung einer Klassenerweiterung verhindern kann, dass die Implementierung auf ivars zugreift. Dazu müssen Sie Ihre Methodenimplementierungen in eine Kategorie einordnen, da der Compiler alle ivar-deklarierenden Klassenerweiterungen sehen muss, bevor er die sieht@implementation
.Option 2 ist völlig falsch. Dies sind globale Variablen, keine Instanzvariablen.
Die Optionen 1 und 3 sind im Wesentlichen identisch. Es macht absolut keinen Unterschied.
Sie können wählen, ob Instanzvariablen in die Header-Datei oder in die Implementierungsdatei eingefügt werden sollen. Der Vorteil der Verwendung der Header-Datei besteht darin, dass Sie über eine schnelle und einfache Tastenkombination (Befehl + Strg + Nach oben in Xcode) verfügen, mit der Sie Ihre Instanzvariablen und die Schnittstellendeklaration anzeigen und bearbeiten können.
Der Nachteil ist, dass Sie die privaten Details Ihrer Klasse in einem öffentlichen Header verfügbar machen. Dies ist in einigen Fällen nicht wünschenswert, insbesondere wenn Sie Code für andere schreiben. Ein weiteres potenzielles Problem besteht darin, dass Sie bei Verwendung von Objective-C ++ vermeiden sollten, C ++ - Datentypen in Ihre Header-Datei aufzunehmen.
Implementierungsinstanzvariablen sind in bestimmten Situationen eine gute Option, aber für den größten Teil meines Codes habe ich die Instanzvariablen immer noch in den Header eingefügt, nur weil es für mich als Codierer, der in Xcode arbeitet, bequemer ist. Mein Rat ist, alles zu tun, was Sie für bequemer halten.
quelle
Dies hat größtenteils mit der Sichtbarkeit des Ivar für Unterklassen zu tun. Unterklassen können nicht auf im
@implementation
Block definierte Instanzvariablen zugreifen .Bei wiederverwendbarem Code, den ich verteilen möchte (z. B. Bibliotheks- oder Framework-Code), bei dem ich keine Instanzvariablen für die öffentliche Überprüfung offenlegen möchte, neige ich dazu, die ivars in den Implementierungsblock zu platzieren (Ihre Option 1).
quelle
Sie sollten Instanzvariablen in einer privaten Schnittstelle über der Implementierung platzieren. Option 3.
Die Dokumentation dazu ist das Programmieren in Objective-C- Handbuch.
Aus der Dokumentation:
quelle
Öffentliche Ivars sollten wirklich als Eigenschaften in der @ Schnittstelle deklariert werden (wahrscheinlich das, woran Sie in 1 denken). Private Ivars können, wenn Sie den neuesten Xcode ausführen und die moderne Laufzeit (64-Bit-OS X oder iOS) verwenden, in der @ -Implementierung (2) deklariert werden und nicht in einer Klassenerweiterung. Ich denke an in 3.
quelle