Welche Best Practices verwenden Sie beim Schreiben von Objective-C und Cocoa? [geschlossen]

346

Ich kenne das HIG (was sehr praktisch ist!), Aber welche Programmierpraktiken verwenden Sie beim Schreiben von Objective-C und insbesondere bei der Verwendung von Cocoa (oder CocoaTouch).

Pixel
quelle
siehe diesen Blog-Beitrag, sehr schön. ironwolf.dangerousgames.com/blog/archives/913
user392412

Antworten:

398

Es gibt einige Dinge, mit denen ich begonnen habe und die ich nicht für Standard halte:

1) Mit dem Aufkommen von Eigenschaften verwende ich nicht mehr "_", um "private" Klassenvariablen voranzustellen. Wenn andere Klassen auf eine Variable zugreifen können, sollte es dann keine Eigenschaft dafür geben? Ich mochte das Präfix "_" immer nicht, um Code hässlicher zu machen, und jetzt kann ich es weglassen.

2) Wenn ich von privaten Dingen spreche, ziehe ich es vor, private Methodendefinitionen in der .m-Datei in einer Klassenerweiterung wie folgt zu platzieren:

#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

Warum die .h-Datei mit Dingen überladen, die Außenstehende nicht interessieren sollten? Das leere () funktioniert für private Kategorien in der .m-Datei und gibt Kompilierungswarnungen aus, wenn Sie die deklarierten Methoden nicht implementieren.

3) Ich habe es mir zur Aufgabe gemacht, Dealloc am Anfang der .m-Datei direkt unter den @synthesize-Anweisungen zu platzieren. Sollte das, was Sie freigeben, nicht ganz oben auf der Liste der Dinge stehen, über die Sie in einer Klasse nachdenken möchten? Dies gilt insbesondere in einer Umgebung wie dem iPhone.

3.5) Machen Sie in Tabellenzellen jedes Element (einschließlich der Zelle selbst) für die Leistung undurchsichtig. Das bedeutet, in allem die richtige Hintergrundfarbe einzustellen.

3.6) Wenn Sie eine NSURLConnection verwenden, möchten Sie in der Regel die Delegate-Methode implementieren:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
      return nil;
}

Ich finde, dass die meisten Webanrufe sehr einzigartig sind und eher die Ausnahme als die Regel sind, nach der Antworten zwischengespeichert werden sollen, insbesondere bei Webdienstanrufen. Durch das Implementieren der gezeigten Methode wird das Zwischenspeichern von Antworten deaktiviert.

Interessant sind auch einige gute iPhone-spezifische Tipps von Joseph Mattiello (erhalten in einer iPhone-Mailingliste). Es gibt noch mehr, aber diese waren die allgemein nützlichsten, die ich dachte (beachten Sie, dass einige Bits jetzt geringfügig vom Original bearbeitet wurden, um Details in den Antworten aufzunehmen):

4) Verwenden Sie nur doppelte Genauigkeit, wenn Sie müssen, z. B. wenn Sie mit CoreLocation arbeiten. Stellen Sie sicher, dass Sie Ihre Konstanten mit 'f' beenden, damit gcc sie als Floats speichert.

float val = someFloat * 2.2f;

Dies ist vor allem dann wichtig, wenn someFloates sich tatsächlich um ein Double handelt. Sie benötigen keine Mathematik im gemischten Modus, da Sie beim Speichern die Genauigkeit von 'val' verlieren. Während Gleitkommazahlen in der Hardware von iPhones unterstützt werden, kann die Ausführung von Arithmetik mit doppelter Genauigkeit im Gegensatz zur einfachen Genauigkeit noch länger dauern. Verweise:

Bei älteren Telefonen arbeiten Berechnungen angeblich mit der gleichen Geschwindigkeit, aber Sie können mehr Komponenten mit einfacher Genauigkeit in Registern als doppelte haben, sodass bei vielen Berechnungen die Genauigkeit mit einfacher Genauigkeit schneller ist.

5) Legen Sie Ihre Eigenschaften als fest nonatomic. Sie sind atomicstandardmäßig und bei der Synthese wird Semaphorcode erstellt, um Multithreading-Probleme zu vermeiden. 99% von Ihnen müssen sich wahrscheinlich keine Sorgen machen, und der Code ist viel weniger aufgebläht und speichereffizienter, wenn er auf nichtatomar eingestellt ist.

6) SQLite kann eine sehr, sehr schnelle Möglichkeit sein, große Datenmengen zwischenzuspeichern. Eine Kartenanwendung kann beispielsweise ihre Kacheln in SQLite-Dateien zwischenspeichern. Der teuerste Teil ist die Festplatten-E / A. Vermeiden Sie viele kleine Schreibvorgänge durch Senden BEGIN;und COMMIT;zwischen großen Blöcken. Wir verwenden zum Beispiel einen 2-Sekunden-Timer, der bei jeder neuen Übermittlung zurückgesetzt wird. Wenn es abläuft, senden wir COMMIT; , was dazu führt, dass alle Ihre Schreibvorgänge in einem großen Block ablaufen. SQLite speichert Transaktionsdaten auf der Festplatte. Durch dieses Begin / End-Wrapping wird die Erstellung vieler Transaktionsdateien vermieden und alle Transaktionen in einer Datei zusammengefasst.

Außerdem blockiert SQL Ihre GUI, wenn sie sich in Ihrem Hauptthread befindet. Wenn Sie eine sehr lange Abfrage haben, ist es eine gute Idee, Ihre Abfragen als statische Objekte zu speichern und Ihre SQL in einem separaten Thread auszuführen. Stellen Sie sicher, dass alle Elemente, die die Datenbank für Abfragezeichenfolgen ändern, in @synchronize() {}Blöcke eingeschlossen werden. Lassen Sie bei kurzen Fragen die Dinge einfach im Haupt-Thread, um die Arbeit zu vereinfachen.

Weitere Tipps zur SQLite-Optimierung finden Sie hier, obwohl das Dokument veraltet erscheint. Viele der Punkte sind wahrscheinlich noch gut.

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

Kendall Helmstetter Gelner
quelle
3
Netter Hinweis zur Doppelarithmetik.
Adam Ernst
8
Klassenerweiterungen sind jetzt die bevorzugte Methode für private Methoden: developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/…
Casebash
9
Ihr Rat über Doppel auf dem iPhone ist veraltet stackoverflow.com/questions/1622729/…
Casebash
3
Nicht veraltet; Ganz falsch: Das ursprünglich unterstützte iPhone schwebt und verdoppelt sich in der Hardware mit ungefähr der gleichen Geschwindigkeit. SQLite speichert auch keine Transaktionen im Speicher. Sie sind auf der Festplatte aufgezeichnet. Nur lange Abfragen blockieren Ihre Benutzeroberfläche. Es ist weniger umständlich, alles im Hauptthread auszuführen und schnellere Abfragen zu verwenden.
tc.
1
@tc: Ich habe das SQL-Element für Transaktionen korrigiert. Beachten Sie, dass ich selbst die letzten vier Elemente nicht geschrieben habe. Ich habe auch klargestellt, dass der Teil über das Verschieben von Abfragen in den Hintergrund nur für sehr lange Abfragen gilt (manchmal kann man sie einfach nicht kürzer machen). Aber das Ganze wegen einiger Punkte "falsch" zu nennen, ist für mich ziemlich extrem. In der obigen Antwort heißt es bereits: "Bei älteren Telefonen arbeiten Berechnungen angeblich mit der gleichen Geschwindigkeit", beachten Sie jedoch den Teil über die größere Anzahl von Einzelgenauigkeitsregistern, die sie immer noch vorzuziehen sind.
Kendall Helmstetter Gelner
109

Verwenden Sie keine unbekannten Zeichenfolgen als Formatzeichenfolgen

Wenn Methoden oder Funktionen ein Formatzeichenfolgenargument verwenden, sollten Sie sicherstellen, dass Sie die Kontrolle über den Inhalt der Formatzeichenfolge haben.

Wenn Sie beispielsweise Zeichenfolgen protokollieren, ist es verlockend, die Zeichenfolgenvariable als einziges Argument an Folgendes zu übergeben NSLog:

    NSString *aString = // get a string from somewhere;
    NSLog(aString);

Das Problem dabei ist, dass die Zeichenfolge Zeichen enthalten kann, die als Formatzeichenfolgen interpretiert werden. Dies kann zu fehlerhaften Ausgaben, Abstürzen und Sicherheitsproblemen führen. Stattdessen sollten Sie die Zeichenfolgenvariable durch eine Formatzeichenfolge ersetzen:

    NSLog(@"%@", aString);
mmalc
quelle
4
Ich bin schon einmal von diesem gebissen worden.
Adam Ernst
Dies ist ein guter Rat für jede Programmiersprache
Tom Fobear
107

Verwenden Sie Standardkonventionen und -terminologien für die Benennung und Formatierung von Kakao und nicht das, was Sie von einer anderen Umgebung gewohnt sind. Es gibt viele Cocoa-Entwickler, und wenn einer von ihnen anfängt, mit Ihrem Code zu arbeiten, ist es viel zugänglicher, wenn er ähnlich aussieht und sich wie anderer Cocoa-Code anfühlt.

Beispiele dafür, was zu tun ist und was nicht:

  • Deklarieren Sie nicht id m_something;in der Schnittstelle eines Objekts und nennen Sie es eine Mitgliedsvariable oder ein Feld . Verwenden Sie somethingoder _somethingfür seinen Namen und nennen Sie es eine Instanzvariable .
  • Nennen Sie keinen Getter -getSomething. Der richtige Kakaoname ist gerecht -something.
  • Nennen Sie keinen Setter -something:. es sollte sein-setSomething:
  • Der Methodenname ist mit den Argumenten durchsetzt und enthält Doppelpunkte. es ist -[NSObject performSelector:withObject:]nicht NSObject::performSelector.
  • Verwenden Sie Inter-Caps (CamelCase) in Methodennamen, Parametern, Variablen, Klassennamen usw. anstelle von Unterstrichen (Unterstrichen).
  • Klassennamen beginnen mit Großbuchstaben, Variablen- und Methodennamen mit Kleinbuchstaben.

Was auch immer Sie tun, verwenden Sie keine ungarische Win16 / Win32-Notation. Sogar Microsoft gab dies mit der Umstellung auf die .NET-Plattform auf.

Chris Hanson
quelle
5
Ich würde argumentieren, benutze setSomething: / überhaupt nicht - benutze stattdessen Eigenschaften. Zu diesem Zeitpunkt gibt es nur wenige Menschen, die wirklich auf Tiger zielen müssen (der einzige Grund, keine Eigenschaften zu nutzen)
Kendall Helmstetter Gelner
18
Eigenschaften generieren weiterhin Zugriffsmethoden für Sie, und mit den Attributen getter = / setter = in der Eigenschaft können Sie die Namen der Methoden angeben. Außerdem können Sie die Syntax [foo Something] anstelle der Syntax foo.something mit Eigenschaften verwenden. Die Benennung von Accessoren ist also weiterhin relevant.
Chris Hanson
3
Dies ist eine großartige Referenz für jemanden, der aus C ++ kommt, wo ich die meisten Dinge getan habe, von denen Sie abraten.
Clinton Blackmore
4
Ein Setter sollte nicht bewirken, dass etwas in der Datenbank gespeichert wird. Es gibt einen Grund, warum Core Data eine -save: -Methode für NSManagedObjectContext hat, anstatt dass Setter sofortige Updates generieren.
Chris Hanson
2
Ich bezweifle, dass dies keine Option war. Möglicherweise musste jedoch Ihre App-Architektur überarbeitet werden. (Um es klar auszudrücken: Ich sage nicht "Sie sollten Kerndaten verwendet haben". Ich sage "Setter sollten nicht in der Datenbank speichern".) Einen Kontext zum Verwalten eines Objektdiagramms haben, anstatt einzelne Objekte darin zu speichern ist praktisch immer sowohl möglich als auch eine bessere Lösung.
Chris Hanson
106

IBOutlets

In der Vergangenheit war die Speicherverwaltung von Steckdosen schlecht. Derzeit wird empfohlen, Verkaufsstellen als Eigenschaften zu deklarieren:

@interface MyClass :NSObject {
    NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end

Durch die Verwendung von Eigenschaften wird die Semantik der Speicherverwaltung deutlich. Es bietet auch ein konsistentes Muster, wenn Sie die Synthese von Instanzvariablen verwenden.

mmalc
quelle
1
Würde das Laden der Feder es dann nicht zweimal behalten? (einmal in Feder, zweitens durch Zuordnung zum Eigentum). Soll ich die im Dealloc freigeben?
Kornel
6
Sie müssen die Steckdosen in viewDidUnload (iPhone OS 3.0+) oder in einer benutzerdefinierten setView: -Methode löschen, um Undichtigkeiten zu vermeiden. Natürlich sollten Sie auch im Dealloc veröffentlichen.
Frank Szczerba
2
Denken Sie daran, dass nicht jeder diesem Stil zustimmt: weblog.bignerdranch.com/?p=95
Michael
So macht es auch Apple. "Beginn der iPhone 3-Entwicklung" erwähnt diese Änderung auch gegenüber früheren Versionen.
Ustun
Ich habe dies in einem anderen Kommentar erwähnt, hätte es aber hier platzieren sollen: Sobald die dynamische Ivar-Synthese für iOS-Apps beginnt (wenn / wann?), Werden Sie froh sein, dass Sie IBOutlet auf das Grundstück im Vergleich zum Ivar setzen!
Joe D'Andrea
97

Verwenden Sie den LLVM / Clang Static Analyzer

HINWEIS: Unter Xcode 4 ist dies jetzt in die IDE integriert.

Sie verwenden den Clang Static Analyzer, um - nicht überraschend - Ihren C- und Objective-C-Code (noch kein C ++) unter Mac OS X 10.5 zu analysieren. Es ist trivial zu installieren und zu verwenden:

  1. Laden Sie die neueste Version von dieser Seite herunter .
  2. Von der Befehlszeile cdin Ihr Projektverzeichnis.
  3. Ausführen scan-build -k -V xcodebuild.

(Es gibt einige zusätzliche Einschränkungen usw., insbesondere sollten Sie ein Projekt in seiner "Debug" -Konfiguration analysieren - siehe http://clang.llvm.org/StaticAnalysisUsage.html für Details - das ist aber mehr oder weniger worauf es ankommt.)

Der Analysator erstellt dann eine Reihe von Webseiten für Sie, die die wahrscheinliche Speicherverwaltung und andere grundlegende Probleme anzeigen, die der Compiler nicht erkennen kann.

mmalc
quelle
1
Ich hatte einige Probleme damit, bis ich diese Anweisungen befolgte
bbrown
15
In XCode 3.2.1 auf Snow Leopard ist es bereits integriert. Sie können es entweder manuell über Run -> Build and Analyze ausführen oder für alle Builds über die Build-Einstellung "Run Static Analyzer" aktivieren. Beachten Sie, dass dieses Tool derzeit nur C und Objective-C unterstützt, nicht jedoch C ++ / Objective-C ++.
Oefe
94

Dies ist subtil, aber praktisch. Wenn Sie sich als Delegierter an ein anderes Objekt übergeben, setzen Sie den Delegierten dieses Objekts vor Ihnen zurück dealloc.

- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}

Auf diese Weise stellen Sie sicher, dass keine Delegatmethoden mehr gesendet werden. Wenn Sie deallocim Äther sind und verschwinden möchten, möchten Sie sicherstellen, dass Ihnen nichts mehr versehentlich Nachrichten senden kann. Denken Sie daran, dass self.someObject von einem anderen Objekt beibehalten werden könnte (es könnte ein Singleton oder im Autorelease-Pool sein oder was auch immer), und bis Sie ihm sagen "Hör auf, mir Nachrichten zu senden!" ist Freiwild.

Wenn Sie sich an diese Gewohnheit gewöhnen, werden Sie vor vielen seltsamen Abstürzen bewahrt, die beim Debuggen schmerzhaft sind.

Das gleiche Prinzip gilt für die Beobachtung von Schlüsselwerten und auch für NSNotifications.

Bearbeiten:

Noch defensiver, ändern:

self.someObject.delegate = NULL;

in:

if (self.someObject.delegate == self)
    self.someObject.delegate = NULL;
schwa
quelle
8
Daran ist nichts Feines, die Dokumentation besagt eindeutig, dass Sie dazu verpflichtet sind. Von Memory Management Programming Guide for Cocoa: Additional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates.
Johne
Es ist besser, nil anstelle von NULL zu verwenden, da NULL den Speicher nicht freigibt.
Naveen Shan
@NaveenShan nil == NULL. Sie sind genau gleich, außer dass dies nilein idund ein NULList void *. Ihre Aussage ist nicht wahr.
@WTP yep, nil == NULL, aber die Verwendung von nil ist eindeutig die bevorzugte Methode. Wenn Sie sich die Beispielcodefragmente von Äpfeln ansehen, verwenden sie überall nil, und wie Sie sagten, ist nil eine ID, die es der Leere vorzuziehen macht * in Fällen, in denen Sie IDs senden, das heißt.
Ahti
1
@Ahti genau, und Nil(Großbuchstaben) ist vom Typ Class*. Obwohl sie alle gleich sind, kann die Verwendung der falschen Fehler kleine Fehler verursachen, insbesondere in Objective-C ++.
86

@ Kendell

Anstatt:

@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end

Verwenden:

@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end

Neu in Objective-C 2.0.

Klassenerweiterungen werden in Apples Objective-C 2.0-Referenz beschrieben.

"Mit Klassenerweiterungen können Sie zusätzliche erforderliche API für eine Klasse an anderen Orten als innerhalb des @ classface-Blocks der Primärklasse deklarieren."

Sie sind also Teil der eigentlichen Klasse - und NICHT zusätzlich zur Klasse eine (private) Kategorie. Subtiler aber wichtiger Unterschied.

schwa
quelle
Sie könnten das tun, aber ich beschreibe es gerne explizit als "privaten" Abschnitt (mehr Dokumentation als funktional), obwohl das natürlich schon offensichtlich ist, wenn es sich in der .m-Datei befindet ...
Kendall Helmstetter Gelner
2
Außer es gibt einen Unterschied zwischen privaten Kategorien und Klassenerweiterungen: "Mit Klassenerweiterungen können Sie zusätzliche erforderliche API für eine Klasse an anderen Orten als innerhalb des @ class-Blocks der Primärklasse deklarieren, wie im folgenden Beispiel dargestellt:" Siehe Link in Bearbeitung.
schwa
Ich bin damit einverstanden, dass es einen Unterschied gibt, bei dem der Compiler Sie warnt, wenn Sie keine CE-Methoden implementiert haben - aber ich finde diesen Aspekt nicht sehr wichtig, wenn sich alle Methoden in derselben Datei befinden und alle privat sind. Ich bevorzuge immer noch den Aspekt der Wartbarkeit beim Markieren des Vorwärtsreferenzblocks als privat
Kendall Helmstetter Gelner
3
Ich sehe (Privat) wirklich nicht als wartbarer als (). Wenn Sie so besorgt sind, kann eine gute Dosis von Kommentaren helfen. Aber natürlich leben und leben lassen. YMMV usw.
schwa
17
Es gibt einen ziemlich wichtigen Vorteil ()anstelle von (Private)(oder einem anderen Kategorienamen): Sie können Eigenschaften als Readwrite neu deklarieren, während sie für die Öffentlichkeit nur schreibgeschützt sind. :)
Pascal
75

Autorelease vermeiden

Da Sie normalerweise (1) keine direkte Kontrolle über ihre Lebensdauer haben, können automatisch freigegebene Objekte vergleichsweise lange bestehen bleiben und den Speicherbedarf Ihrer Anwendung unnötig erhöhen. Während dies auf dem Desktop von geringer Bedeutung sein kann, kann dies auf eingeschränkteren Plattformen ein erhebliches Problem sein. Daher wird es auf allen Plattformen und insbesondere auf eingeschränkteren Plattformen als bewährte Methode angesehen, die Verwendung von Methoden zu vermeiden, die zu automatisch freigegebenen Objekten führen würden. Stattdessen wird empfohlen, das Alloc / Init-Muster zu verwenden.

Also eher als:

aVariable = [AClass convenienceMethod];

Wenn möglich, sollten Sie stattdessen Folgendes verwenden:

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];

Wenn Sie Ihre eigenen Methoden schreiben, die ein neu erstelltes Objekt zurückgeben, können Sie die Namenskonvention von Cocoa nutzen , um dem Empfänger anzuzeigen, dass es freigegeben werden muss, indem Sie dem Methodennamen "new" voranstellen.

Also anstelle von:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}

du könntest schreiben:

- (MyClass *)newInstance {
    MyClass *instance = [[self alloc] init];
    // configure instance
    return instance;
}

Da der Methodenname mit "neu" beginnt, wissen die BenutzernewObject Ihrer API, dass sie für die Freigabe des empfangenen Objekts verantwortlich sind (siehe z. B. die Methode von NSObjectController ).

(1) Sie können die Kontrolle übernehmen, indem Sie Ihre eigenen lokalen Autorelease-Pools verwenden. Weitere Informationen hierzu finden Sie unter Autorelease-Pools .

mmalc
quelle
6
Ich finde die Vorteile der nicht mit Autorelease überwiegt die Kosten (dh mehr Speicherverlust Bugs). Der Code im Hauptthread sollte ohnehin ziemlich kurz sein (andernfalls wird die Benutzeroberfläche eingefroren), und für länger laufenden, speicherintensiven Hintergrundcode können Sie die speicherintensiven Teile jederzeit in lokale Autorelease-Pools einbinden.
Adib
56
Ich stimme dir nicht zu. Sie sollten nach Möglichkeit automatisch freigegebene Objekte verwenden. Wenn sie den Speicherbedarf um zu viel erhöhen, sollten Sie einen anderen verwenden NSAutoreleasePool. Aber erst nachdem Sie bestätigt haben, dass dies wirklich ein Problem ist. Vorzeitige Optimierung und all das ...
Sven
3
Ich verbringe weniger als 40 Sekunden. Ein Tag, an dem ich [someObject release] eingegeben und die "zusätzliche Zeile" gelesen habe, als ich ein neues Objekt instanziiert habe, aber ich habe einmal 17 Stunden durchgebrannt, um einen Autorelease-Fehler zu finden, der nur in besonderen Fällen angezeigt wurde und keinen zusammenhängenden Fehler in der Konsole ergab. Daher stimme ich adib zu, wenn er sagt: "Ich finde, die Vorteile der Nichtverwendung von Autorelease überwiegen die Kosten."
RickiG
7
Ich stimme Sven zu. Das Hauptziel sollte die Klarheit des Codes und die Reduzierung von Codierungsfehlern sein, wobei die Speicheroptimierung nur dort erfolgt, wo sie benötigt wird. Das Eingeben einer [[[Foo alloc] init] autorelease] ist schnell und Sie haben sofort das Problem, dieses neue Objekt freizugeben. Wenn Sie den Code lesen, müssen Sie nicht nach der entsprechenden Version suchen, um sicherzustellen, dass sie nicht durchgesickert ist.
Mike Weller
3
Der Lebenszyklus von automatisch freigegebenen Objekten ist gut definiert und auf ausreichendem Niveau bestimmbar.
Eonil
69

Einige davon wurden bereits erwähnt, aber hier ist, was ich mir auf den ersten Blick vorstellen kann:

  • Befolgen Sie die KVO-Namensregeln. Auch wenn Sie KVO jetzt nicht verwenden, ist es meiner Erfahrung nach oftmals auch in Zukunft von Vorteil. Und wenn Sie KVO oder Bindungen verwenden, müssen Sie wissen, dass die Dinge so funktionieren, wie sie sollen. Dies umfasst nicht nur Zugriffsmethoden und Instanzvariablen, sondern auch zu viele Beziehungen, Validierung, automatische Benachrichtigung abhängiger Schlüssel usw.
  • Ordnen Sie private Methoden einer Kategorie zu. Nicht nur die Schnittstelle, sondern auch die Implementierung. Es ist gut, konzeptionell eine gewisse Distanz zwischen privaten und nicht privaten Methoden zu haben. Ich füge alles in meine .m-Datei ein.
  • Fügen Sie Hintergrund-Thread-Methoden in eine Kategorie ein. Das gleiche wie oben. Ich habe festgestellt, dass es gut ist, eine klare konzeptionelle Barriere beizubehalten, wenn Sie darüber nachdenken, was auf dem Haupt-Thread steht und was nicht.
  • Verwenden Sie #pragma mark [section]. Normalerweise gruppiere ich nach meinen eigenen Methoden, den Überschreibungen jeder Unterklasse und allen Informationen oder formalen Protokollen. Dies macht es viel einfacher, genau zu dem zu springen, wonach ich suche. Gruppieren Sie zum gleichen Thema ähnliche Methoden (wie die Delegatmethoden einer Tabellenansicht) und kleben Sie sie nicht einfach irgendwo hin.
  • Stellen Sie privaten Methoden und ivars _ voran. Ich mag die Art und Weise, wie es aussieht, und ich benutze weniger wahrscheinlich einen Ivar, wenn ich versehentlich eine Immobilie meine.
  • Verwenden Sie keine Mutator-Methoden / -Eigenschaften in init & dealloc. Ich habe noch nie etwas Schlimmes erlebt, aber ich kann die Logik erkennen, wenn Sie die Methode ändern, um etwas zu tun, das vom Zustand Ihres Objekts abhängt.
  • Fügen Sie IBOutlets in Eigenschaften ein. Ich habe das hier gerade gelesen, aber ich werde damit anfangen. Unabhängig von den Vorteilen des Gedächtnisses scheint es stilistisch besser zu sein (zumindest für mich).
  • Vermeiden Sie das Schreiben von Code, den Sie nicht unbedingt benötigen. Dies deckt wirklich viele Dinge ab, wie das Erstellen von Ivars nach #defineBelieben oder das Zwischenspeichern eines Arrays, anstatt es jedes Mal zu sortieren, wenn die Daten benötigt werden. Ich könnte viel dazu sagen, aber unter dem Strich schreiben Sie keinen Code, bis Sie ihn benötigen oder der Profiler Sie dazu auffordert. Es macht die Wartung auf lange Sicht viel einfacher.
  • Beenden Sie, was Sie beginnen. Eine Menge halbfertigen, fehlerhaften Codes zu haben, ist der schnellste Weg, ein Projekt tot zu töten. Wenn Sie eine Stub-Methode benötigen, die in Ordnung ist, geben Sie sie einfach an, indem Sie sie NSLog( @"stub" )einfügen, oder Sie möchten den Überblick behalten.
Marc Charbonneau
quelle
3
Ich würde vorschlagen, dass Sie private Methoden in eine Klassenfortsetzung einfügen. (dh @interface MyClass () ... @end in Ihrer .m)
Jason Medeiros
3
Anstelle von #PRAGMA können Sie einen Kommentar verwenden // Mark: [Section], der portabler ist und identisch funktioniert.
Aleemb
Sofern mir keine spezielle Syntax fehlt, // Mark: fügt keine Beschriftung in das Dropdown-Menü für Xcode-Funktionen ein, was wirklich der halbe Grund für die Verwendung ist.
Marc Charbonneau
6
Sie müssen den Großbuchstaben "// MARK: ..." verwenden, damit er in der Dropdown-Liste angezeigt wird.
Rhult
3
In Bezug auf können Finish what you startSie auch // TODO:Code zum Vervollständigen markieren, der in der Dropdown-Liste angezeigt wird.
Ich wurde
56

Schreiben Sie Unit-Tests. Sie können viele Dinge in Cocoa testen , die in anderen Frameworks möglicherweise schwieriger sind. Mit UI-Code können Sie beispielsweise im Allgemeinen überprüfen, ob die Dinge so verbunden sind, wie sie sein sollten, und darauf vertrauen, dass sie bei Verwendung funktionieren. Und Sie können Status- und Delegatenmethoden einfach einrichten, um sie zu testen.

Sie haben auch keine Sichtbarkeit von öffentlichen oder geschützten oder privaten Methoden, die das Schreiben von Tests für Ihre Interna behindert.

Chris Hanson
quelle
Welche Test-Frameworks empfehlen Sie?
Melfar
13
Xcode enthält OCUnit, ein Objective-C-Unit-Test-Framework, und Unterstützung für die Ausführung von Bundles von Unit-Tests als Teil Ihres Erstellungsprozesses.
Chris Hanson
55

Goldene Regel: Wenn Sie allocdann Sie release!

UPDATE: Es sei denn, Sie verwenden ARC

logancautrell
quelle
26
Auch wenn Sie copy, mutableCopy, newoder retain.
Sven
54

Schreiben Sie Objective-C nicht so, als wäre es Java / C # / C ++ / etc.

Ich habe einmal gesehen, wie ein Team, das Java EE-Webanwendungen geschrieben hat, versucht hat, eine Cocoa-Desktopanwendung zu schreiben. Als wäre es eine Java EE-Webanwendung. Es flogen viele AbstractFooFactory und FooFactory und IFoo und Foo herum, wenn alles, was sie wirklich brauchten, eine Foo-Klasse und möglicherweise ein Fooable-Protokoll war.

Um sicherzustellen, dass Sie dies nicht tun, müssen Sie die Unterschiede in der Sprache wirklich verstehen. Beispielsweise benötigen Sie die oben genannten abstrakten Factory- und Factory-Klassen nicht, da Objective-C-Klassenmethoden genauso dynamisch wie Instanzmethoden ausgelöst werden und in Unterklassen überschrieben werden können.

Chris Hanson
quelle
10
Als Java-Entwickler, der eine abstrakte Factory in Objective-C geschrieben hat, finde ich das faszinierend. Würde es Ihnen etwas ausmachen, etwas mehr zu erklären, wie dies funktioniert - vielleicht anhand eines Beispiels?
Teabot
2
Glauben Sie immer noch, dass wir nach all der Zeit, die vergangen ist, seit Sie diese Antwort gepostet haben, keine abstrakten Fabrikklassen brauchen?
Kirk.burleson
50

Stellen Sie sicher, dass Sie die Seite Debugging Magic mit einem Lesezeichen versehen . Dies sollte Ihre erste Station sein, wenn Sie Ihren Kopf gegen eine Wand schlagen, während Sie versuchen, die Quelle eines Kakaobugs zu finden.

Beispielsweise erfahren Sie, wie Sie die Methode finden, bei der Sie zuerst Speicher zugewiesen haben, der später zu Abstürzen führt (z. B. während der App-Beendigung).

mj1531
quelle
1
Es gibt jetzt eine iOS-spezifische Version der Debugging Magic- Seite.
Jeethu
38

Versuchen Sie zu vermeiden, was ich jetzt beschlossen habe, Newbiecategoryaholism zu nennen. Wenn Neulinge in Objective-C Kategorien entdecken, werden sie oft wild und fügen jeder existierenden Klasse nützliche kleine Kategorien hinzu ( "Was? Ich kann eine Methode hinzufügen, um eine Zahl in römische Zahlen in NSNumber Rock on umzuwandeln!" ).

Tu das nicht.

Ihr Code wird portabler und verständlicher, ohne dass Dutzende kleiner Kategoriemethoden auf zwei Dutzend Grundklassen verteilt sind.

Wenn Sie wirklich glauben, dass Sie eine Kategoriemethode benötigen, um Code zu optimieren, werden Sie die Methode meistens nie wiederverwenden.

Es gibt auch andere Gefahren, es sei denn, Sie benennen Ihre Kategoriemethoden (und wer ist neben dem absolut verrückten Ddribin?), Besteht die Möglichkeit, dass Apple, ein Plugin oder etwas anderes, das in Ihrem Adressraum ausgeführt wird, dieselbe Kategorie definiert Methode mit dem gleichen Namen mit einer etwas anderen Nebenwirkung ....

OK. Nachdem Sie gewarnt wurden, ignorieren Sie das "Mach diesen Teil nicht". Aber üben Sie extreme Zurückhaltung.

schwa
quelle
Ich mag Ihre Antwort, mein Rat würde darin bestehen, keine Kategorie zum Speichern von Dienstprogrammcode zu verwenden, es sei denn, Sie replizieren einen Code an mehr als einer Stelle und der Code gehört eindeutig zu der Klasse, die Sie kategorisieren möchten ...
Kendall Helmstetter Gelner
Ich möchte nur meine Unterstützung für Methoden für Namespace-Kategorien einbringen und zum Ausdruck bringen. Es scheint einfach das Richtige zu sein.
Michael Buckley
+1 wenn auch nur für die römischen Ziffern. Das würde ich total machen!
Brian Postow
14
Kontrapunkt: In den letzten anderthalb Jahren habe ich genau das Gegenteil befolgt: "Wenn es in einer Kategorie implementiert werden kann, tun Sie dies." Infolgedessen ist mein Code viel prägnanter, ausdrucksvoller und leichter zu lesen als der ausführliche Beispielcode, den Apple bereitstellt. Ich habe insgesamt etwa 10 Minuten durch einen Namespace-Konflikt verloren und wahrscheinlich Mannmonate durch die Effizienz gewonnen, die ich für mich selbst geschaffen habe. Jedem sein eigenes, aber ich habe diese Politik übernommen und kenne die Risiken, und ich bin äußerst froh, dass ich das getan habe.
Cduhn
7
Ich stimme nicht zu Wenn es sich um eine Funktion handelt, die für ein Foundation-Objekt gilt und Sie sich einen guten Namen vorstellen können, fügen Sie ihn in eine Kategorie ein. Ihr Code ist besser lesbar. Ich denke, dass der wichtigste Punkt hier ist: alles in Maßen tun.
mxcl
37

Widerstehen Sie der Unterklasse der Welt. In Cocoa wird viel durch Delegieren und Verwenden der zugrunde liegenden Laufzeit erreicht, in anderen Frameworks durch Unterklassen.

In Java verwenden Sie beispielsweise häufig Instanzen anonymer *ListenerUnterklassen, und in .NET verwenden Sie häufig Ihre EventArgsUnterklassen. In Cocoa tun Sie dies auch nicht - stattdessen wird die Zielaktion verwendet.

Chris Hanson
quelle
6
Sonst bekannt als "Zusammensetzung über Vererbung".
Andrew Ebling
37

Sortieren Sie die Zeichenfolgen nach Wunsch des Benutzers

Wenn Sie Zeichenfolgen sortieren, die dem Benutzer angezeigt werden sollen, sollten Sie nicht die einfache compare:Methode verwenden. Stattdessen sollten Sie immer lokalisierte Vergleichsmethoden wie localizedCompare:oder verwenden localizedCaseInsensitiveCompare:.

Weitere Informationen finden Sie unter Suchen, Vergleichen und Sortieren von Zeichenfolgen .

mmalc
quelle
31

Deklarierte Eigenschaften

Normalerweise sollten Sie die Funktion "Deklarierte Objective-C 2.0-Eigenschaften" für alle Ihre Eigenschaften verwenden. Wenn sie nicht öffentlich sind, fügen Sie sie in einer Klassenerweiterung hinzu. Durch die Verwendung deklarierter Eigenschaften wird die Speicherverwaltungssemantik sofort klar und Sie können Ihre Dealloc-Methode leichter überprüfen. Wenn Sie Ihre Eigenschaftsdeklarationen gruppieren, können Sie sie schnell scannen und mit der Implementierung Ihrer Dealloc-Methode vergleichen.

Sie sollten gründlich überlegen, bevor Sie Eigenschaften nicht als "nichtatomar" markieren. Wie im Handbuch zur Programmiersprache Objective C erwähnt , sind Eigenschaften standardmäßig atomar und verursachen einen erheblichen Overhead. Darüber hinaus macht es Ihre Anwendung nicht threadsicher, wenn Sie einfach alle Ihre Eigenschaften atomar machen. Beachten Sie natürlich auch, dass Sie, wenn Sie nicht 'nichtatomar' angeben und Ihre eigenen Zugriffsmethoden implementieren (anstatt sie zu synthetisieren), diese atomar implementieren müssen.

mmalc
quelle
26

Denken Sie an Nullwerte

Wie diese Frage vermerkt, werden Nachrichten annil in Objective-C gültig. Obwohl dies häufig von Vorteil ist und zu saubererem und natürlicherem Code führt, kann die Funktion gelegentlich zu eigenartigen und schwer auffindbaren Fehlern führen, wenn Sie einen nilWert erhalten, den Sie nicht erwartet haben.

mmalc
quelle
Ich habe das: #define SXRelease(o); o = nilund das gleiche für CFReleaseund free. Das vereinfacht alles.
26

Verwenden Sie NSAssert und Freunde. Ich benutze die ganze Zeit nil als gültiges Objekt ... besonders das Senden von Nachrichten an nil ist in Obj-C vollkommen gültig. Wenn ich jedoch wirklich den Status einer Variablen überprüfen möchte, verwende ich NSAssert und NSParameterAssert, um Probleme leicht aufzuspüren.

NikWest
quelle
23

Einfach, aber oft vergessen. Nach Spezifikation:

Im Allgemeinen müssen Methoden in verschiedenen Klassen mit demselben Selektor (demselben Namen) auch denselben Rückgabe- und Argumenttyp verwenden. Diese Einschränkung wird vom Compiler auferlegt, um eine dynamische Bindung zu ermöglichen.

In diesem Fall wird davon ausgegangen, dass alle gleichnamigen Selektoren, auch wenn sie sich in verschiedenen Klassen befinden , identische Rückgabe- / Argumenttypen haben. Hier ist ein einfaches Beispiel.

@interface FooInt:NSObject{}
-(int) print;
@end

@implementation FooInt
-(int) print{
    return 5;
}
@end

@interface FooFloat:NSObject{}
-(float) print;
@end

@implementation FooFloat
-(float) print{
    return 3.3;
}
@end

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
    id f1=[[FooFloat alloc]init];
    //prints 0, runtime considers [f1 print] to return int, as f1's type is "id" and FooInt precedes FooBar
    NSLog(@"%f",[f1 print]);

    FooFloat* f2=[[FooFloat alloc]init];
    //prints 3.3 expectedly as the static type is FooFloat
    NSLog(@"%f",[f2 print]);

    [f1 release];
    [f2 release]
    [pool drain];

    return 0;
}   
Kontrolle
quelle
es ist leicht zu vergessen. Trotzdem wichtig
Brock Woolf
3
Dies ist nur dann ein Problem, wenn Sie auf statische Eingaben verzichten. Wenn der Compiler den Typ kennt, können sich Argument- und Rückgabetypen problemlos unterscheiden. Persönlich finde ich das nicht oft ein Problem. Apple hat auch viele Methoden, die den gleichen Namen haben, sich jedoch in den Rückgabetypen unterscheiden. Schließlich gibt es ein Compiler-Flag, das Sie in mehrdeutigen Fällen warnt.
Nikolai Ruhe
Wenn wir die Richtlinien der Apple zur Namenskonvention befolgen, wird diese Situation nicht eintreten :)
Eonil
22

Wenn Sie Leopard (Mac OS X 10.5) oder höher verwenden, können Sie mit der Anwendung Instruments Speicherlecks suchen und verfolgen. Wählen Sie nach dem Erstellen Ihres Programms in Xcode Ausführen> Mit Leistungstool starten> Lecks.

Auch wenn Ihre App keine Lecks aufweist, halten Sie Objekte möglicherweise zu lange in der Nähe. In Instruments können Sie hierfür das ObjectAlloc-Instrument verwenden. Wählen Sie das ObjectAlloc-Instrument in Ihrem Instrumentendokument aus und rufen Sie die Details des Instruments auf (falls es noch nicht angezeigt wird), indem Sie Ansicht> Detail wählen (daneben sollte ein Häkchen angezeigt werden). Stellen Sie unter "Allocation Lifespan" im ObjectAlloc-Detail sicher, dass Sie das Optionsfeld neben "Created & Still Living" aktivieren.

Wenn Sie jetzt die Aufzeichnung Ihrer Anwendung beenden, zeigt Ihnen die Auswahl des ObjectAlloc-Tools in der Spalte "# Net" an, wie viele Verweise auf jedes noch lebende Objekt in Ihrer Anwendung vorhanden sind. Stellen Sie sicher, dass Sie nicht nur Ihre eigenen Klassen betrachten, sondern auch die Klassen der Objekte der obersten Ebene Ihrer NIB-Dateien. Wenn Sie beispielsweise keine Fenster auf dem Bildschirm haben und Verweise auf ein noch lebendes NSW-Fenster sehen, haben Sie es möglicherweise nicht in Ihrem Code veröffentlicht.

mj1531
quelle
21

Aufräumen im Dealloc.

Dies ist eines der am einfachsten zu vergessenden Dinge - insb. beim Codieren bei 150mph. Bereinigen Sie immer, immer, immer Ihre Attribute / Mitgliedsvariablen im Dealloc.

Ich verwende gerne Objc 2-Attribute - mit der neuen Punktnotation -, sodass die Bereinigung schmerzlos ist. Oft so einfach wie:

- (void)dealloc
{
    self.someAttribute = NULL;
    [super dealloc];
}

Dies kümmert sich um die Freigabe für Sie und setzt das Attribut auf NULL (was ich als defensive Programmierung betrachte - falls eine andere Methode weiter unten im Dealloc erneut auf die Mitgliedsvariable zugreift - selten, aber möglich möglich).

Wenn GC in 10.5 aktiviert ist, wird dies nicht mehr so ​​oft benötigt. Möglicherweise müssen Sie jedoch noch andere von Ihnen erstellte Ressourcen bereinigen. Dies können Sie stattdessen in der Finalisierungsmethode tun.

schwa
quelle
12
Im Allgemeinen sollten Sie keine Accessor-Methoden in dealloc (oder init) verwenden.
Mmalc
1
Abgesehen von Leistungsgründen (Accessoren sind etwas langsamer als Direktzugriff), warum sollte ich Accessoren nicht in Dealloc oder Init verwenden?
schwa
1
(a) Leistungsgründe sind für sich genommen völlig ausreichend (insbesondere wenn Ihre Accessoren atomar sind). (b) Sie sollten mögliche Nebenwirkungen vermeiden, die Accessoren haben können. Letzteres ist insbesondere dann ein Problem, wenn Ihre Klasse untergeordnet sein kann.
Mmalc
3
Ich werde bemerken, dass Sie Accessoren in Dealloc verwenden müssen , wenn Sie auf der modernen Laufzeit mit synthetisierten Ivars arbeiten. Viel moderner Laufzeitcode ist GC, aber nicht alles.
Louis Gerbarg
1
Eine ausführlichere Ansicht darüber, ob Accessor-Methoden / -Eigenschaften in -initund -deallocMethoden verwendet werden sollen oder nicht, finden Sie hier: mikeash.com/?page=pyblog/…
Johan Kool
17

Alle diese Kommentare sind großartig, aber ich bin wirklich überrascht, dass niemand den vor einiger Zeit veröffentlichten Objective-C Style Guide von Google erwähnt hat . Ich denke, sie haben sehr gründliche Arbeit geleistet.

slf
quelle
7
Hmm, das erste Beispiel ist schon voller Scheiße. Dokumentieren Sie niemals Sprachsprachen. Wenn ich solche Kommentare in einer Header-Datei finden würde, würde ich nicht weiterlesen.
Stephan Eggermont
5
Oh meine Augen !!!!! Ich kann nicht glauben, was ich gesehen habe.
Eonil
13

Vergessen Sie nicht, dass NSWindowController und NSViewController die Objekte der obersten Ebene der von ihnen verwalteten NIB-Dateien freigeben.

Wenn Sie eine NIB-Datei manuell laden, sind Sie dafür verantwortlich, die Objekte der obersten Ebene der NIB freizugeben, wenn Sie damit fertig sind.

mj1531
quelle
12

Eine ziemlich offensichtliche Option für Anfänger: Verwenden Sie die automatische Einrückungsfunktion von Xcode für Ihren Code. Selbst wenn Sie aus einer anderen Quelle kopieren / einfügen, können Sie nach dem Einfügen des Codes den gesamten Codeblock auswählen, mit der rechten Maustaste darauf klicken und dann die Option auswählen, alles in diesem Block erneut einzurücken.

Xcode analysiert diesen Abschnitt tatsächlich und rückt ihn basierend auf Klammern, Schleifen usw. ein. Es ist viel effizienter, als die Leertaste oder die Tabulatortaste für jede einzelne Zeile zu drücken.

iWasRobbed
quelle
Sie können Tab sogar auf Einzug setzen und dann Cmd-A und Tab ausführen.
Plumenator
10

Ich weiß, dass ich dies übersehen habe, als ich zum ersten Mal in die Kakaoprogrammierung eingestiegen bin.

Stellen Sie sicher, dass Sie die Verantwortlichkeiten für die Speicherverwaltung in Bezug auf NIB-Dateien verstehen. Sie sind dafür verantwortlich, die Objekte der obersten Ebene in jeder von Ihnen geladenen NIB-Datei freizugeben. Lesen Sie die Apple-Dokumentation zu diesem Thema.

mj1531
quelle
6
Das ist nicht wahr. Ob Sie für die Freigabe von Objekten der obersten Ebene verantwortlich sind oder nicht, hängt davon ab, von welcher Klasse Sie erben und welche Plattform Sie verwenden. Siehe unter anderem developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/… .
Mmalc
10

Aktivieren Sie alle GCC-Warnungen und deaktivieren Sie diejenigen, die regelmäßig durch Apples Header verursacht werden, um das Rauschen zu reduzieren.

Führen Sie außerdem häufig eine statische Clang-Analyse durch. Sie können es für alle Builds über die Build-Einstellung "Run Static Analyzer" aktivieren.

Schreiben Sie Komponententests und führen Sie sie bei jedem Build aus.

oefe
quelle
Und wenn Sie können, aktivieren Sie "Warnungen als Fehler behandeln". Lassen Sie keine Warnung zu.
Peter Hosey
2
Ein praktisches Skript zum Einrichten Ihres Projekts mit empfohlenen Warnungen finden Sie hier: rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scpt
Johan Kool
10

Variablen und Eigenschaften

1 / Halten Sie Ihre Header sauber und verstecken Sie die Implementierung
Fügen Sie keine Instanzvariablen in Ihren Header ein. Private Variablen, die als Eigenschaften in die Klassenfortsetzung eingefügt werden. Öffentliche Variablen werden in Ihrem Header als öffentliche Eigenschaften deklariert. Wenn es nur gelesen werden soll, deklarieren Sie es als schreibgeschützt und überschreiben Sie es als schreibgeschützt in der Klassenfortsetzung. Grundsätzlich verwende ich überhaupt keine Variablen, sondern nur Eigenschaften.

2 / Geben Sie Ihren Eigenschaften einen nicht standardmäßigen Variablennamen, Beispiel:


@synthesize property = property_;

Grund 1: Sie werden Fehler feststellen, die durch das Vergessen von "Selbst" verursacht werden. bei der Zuweisung der Eigenschaft. Grund 2: Aus meinen Experimenten geht hervor, dass Leak Analyzer in Instruments Probleme hat, undichte Eigenschaften mit dem Standardnamen zu erkennen.

3 / Verwenden Sie Retain oder Release niemals direkt auf Eigenschaften (oder nur in sehr außergewöhnlichen Situationen). Weisen Sie ihnen in Ihrem Dealloc einfach eine Null zu. Retain-Eigenschaften sollen das Retain / Release von sich aus handhaben. Sie wissen nie, ob ein Setter beispielsweise keine Beobachter hinzufügt oder entfernt. Sie sollten die Variable nur direkt in ihrem Setter und Getter verwenden.

Ansichten

1 / Fügen Sie jede Ansichtsdefinition in eine xib ein, wenn Sie können (die Ausnahme sind normalerweise dynamische Inhalts- und Ebeneneinstellungen). Das spart Zeit (es ist einfacher als das Schreiben von Code), lässt sich leicht ändern und hält Ihren Code sauber.

2 / Versuchen Sie nicht, Ansichten zu optimieren, indem Sie die Anzahl der Ansichten verringern. Erstellen Sie UIImageView nicht in Ihrem Code anstelle von xib, nur weil Sie Unteransichten hinzufügen möchten. Verwenden Sie stattdessen UIImageView als Hintergrund. Das Ansichtsframework kann problemlos Hunderte von Ansichten verarbeiten.

3 / IBOutlets müssen nicht immer beibehalten werden (oder stark sein). Beachten Sie, dass die meisten Ihrer IBOutlets Teil Ihrer Ansichtshierarchie sind und daher implizit beibehalten werden.

4 / Geben Sie alle IBOutlets in viewDidUnload frei

5 / Rufen Sie viewDidUnload von Ihrer Dealloc-Methode auf. Es wird nicht implizit genannt.

Erinnerung

1 / Autorelease-Objekte beim Erstellen. Viele Fehler werden verursacht, wenn Sie Ihren Release-Aufruf in einen if-else-Zweig oder nach einer return-Anweisung verschieben. Release anstelle von Autorelease sollte nur in Ausnahmesituationen verwendet werden - z. B. wenn Sie auf einen Runloop warten und nicht möchten, dass Ihr Objekt zu früh automatisch freigegeben wird.

2 / Auch wenn Sie Authomatic Reference Counting verwenden, müssen Sie genau wissen, wie Retain-Release-Methoden funktionieren. Die manuelle Verwendung von Retain-Release ist nicht komplizierter als ARC. In beiden Fällen müssen Sie sich mit Lecks und Retain-Zyklen befassen. Verwenden Sie Retain-Release manuell für große Projekte oder komplizierte Objekthierarchien.

Bemerkungen

1 / Machen Sie Ihren Code automatisch dokumentiert. Jeder Variablenname und Methodenname sollte angeben, was er tut. Wenn der Code korrekt geschrieben ist (Sie benötigen viel Übung), benötigen Sie keine Codekommentare (nicht dasselbe wie Dokumentationskommentare). Algorithmen können kompliziert sein, aber der Code sollte immer einfach sein.

2 / Manchmal benötigen Sie einen Kommentar. Normalerweise, um ein nicht offensichtliches Codeverhalten oder einen Hack zu beschreiben. Wenn Sie der Meinung sind, dass Sie einen Kommentar schreiben müssen, versuchen Sie zunächst, den Code so umzuschreiben, dass er einfacher und ohne Kommentare ist.

Vertiefung

1 / Erhöhen Sie die Einrückung nicht zu stark. Der größte Teil Ihres Methodencodes sollte auf Methodenebene eingerückt sein. Verschachtelte Blöcke (wenn, für usw.) verringern die Lesbarkeit. Wenn Sie drei verschachtelte Blöcke haben, sollten Sie versuchen, die inneren Blöcke in eine separate Methode zu verschieben. Vier oder mehr verschachtelte Blöcke sollten niemals verwendet werden. Wenn sich der größte Teil Ihres Methodencodes in einem if befindet, negieren Sie die if-Bedingung. Beispiel:


if (self) {
   //... long initialization code ...
}

return self;

if (!self) {
   return nil;
}

//... long initialization code ...

return self;

Verstehen Sie C-Code, hauptsächlich C-Strukturen

Beachten Sie, dass Obj-C nur eine leichte OOP-Ebene über der C-Sprache ist. Sie sollten verstehen, wie grundlegende Codestrukturen in C funktionieren (Aufzählungen, Strukturen, Arrays, Zeiger usw.). Beispiel:


view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);

ist das gleiche wie:


CGRect frame = view.frame;
frame.size.height += 20;
view.frame = frame;

Und viele mehr

Bewahren Sie Ihr eigenes Dokument mit Codierungsstandards auf und aktualisieren Sie es häufig. Versuchen Sie, aus Ihren Fehlern zu lernen. Verstehen Sie, warum ein Fehler erstellt wurde, und versuchen Sie, ihn mithilfe von Codierungsstandards zu vermeiden.

Unsere Codierungsstandards umfassen derzeit etwa 20 Seiten, eine Mischung aus Java-Codierungsstandards, Google Obj-C / C ++ - Standards und unseren eigenen Ergänzungen. Dokumentieren Sie Ihren Code, verwenden Sie Standard-Einrückungen, Leerzeichen und Leerzeilen an den richtigen Stellen usw.

Sulthan
quelle
9

Seien Sie funktionaler .

Objective-C ist eine objektorientierte Sprache, die jedoch den Funktionsstil des Cocoa-Frameworks berücksichtigt und in vielen Fällen als Funktionsstil konzipiert ist.

  1. Es gibt eine Trennung der Veränderlichkeit. Verwenden Sie unveränderliche Klassen als primäres und veränderliches Objekt als sekundäres. Verwenden Sie beispielsweise hauptsächlich NSArray und NSMutableArray nur bei Bedarf.

  2. Es gibt reine Funktionen. Nicht so viele, kaufen viele Framework-APIs sind wie reine Funktion konzipiert. Schauen Sie sich Funktionen wie CGRectMake()oder an CGAffineTransformMake(). Offensichtlich sieht die Zeigerform effizienter aus. Indirekte Argumente mit Zeigern können jedoch keine Nebenwirkung bieten. Entwerfen Sie Strukturen so weit wie möglich. Trennen Sie gerade Statusobjekte. Verwenden Sie -copyanstelle von, -retainwenn Sie einen Wert an ein anderes Objekt übergeben. Weil der gemeinsame Zustand die Mutation zum Wert in einem anderen Objekt stillschweigend beeinflussen kann. Kann also nicht nebenwirkungsfrei sein. Wenn Sie einen Wert von extern von Objekt haben, kopieren Sie ihn. Daher ist es auch wichtig, den gemeinsamen Status so minimal wie möglich zu gestalten.

Haben Sie jedoch keine Angst davor, auch unreine Funktionen zu verwenden.

  1. Es gibt eine faule Bewertung. Sehen Sie so etwas wie -[UIViewController view]Eigentum. Die Ansicht wird nicht erstellt, wenn das Objekt erstellt wird. Es wird erstellt, wenn der Anrufer die viewEigenschaft zum ersten Mal liest . UIImagewird erst geladen, wenn es tatsächlich gezeichnet wird. Es gibt viele Implementierungen wie dieses Design. Diese Art von Designs ist sehr hilfreich für das Ressourcenmanagement, aber wenn Sie das Konzept der verzögerten Bewertung nicht kennen, ist es nicht einfach, das Verhalten dieser Designs zu verstehen.

  2. Es gibt Schließung. Verwenden Sie so oft wie möglich C-Blöcke. Dies wird Ihr Leben erheblich vereinfachen. Lesen Sie jedoch noch einmal die Blockspeicherverwaltung, bevor Sie sie verwenden.

  3. Es gibt halbautomatische GC. NSAutoreleasePool. Verwenden Sie -autoreleaseprimäre. Verwenden Sie die manuelle -retain/-releaseSekundärseite, wenn Sie sie wirklich brauchen. (Beispiel: Speicheroptimierung, explizites Löschen von Ressourcen)

Eonil
quelle
2
Zu 3) Ich werde den umgekehrten Ansatz vorschlagen: Verwenden Sie, wo immer möglich, die manuelle Aufbewahrung / Freigabe! Wer weiß, wie dieser Code verwendet wird - und wenn er in einer engen Schleife verwendet wird, kann dies Ihre Speichernutzung unnötig in die Luft jagen.
Eiko
@Eiko Das ist nur eine vorzeitige Optimierung , kann keine allgemeine Anleitung sein.
Eonil
1
Ich denke, es ist eher eine Design-Sache, besonders wenn man an Modellklassen arbeitet. Ich betrachte das Wachsen des Gedächtnisses als Nebeneffekt, und das möchte ich nicht oft zeigen. Schlimmer noch, ein anderer Entwickler, der meinen Code verwendet, hat keine andere Wahl, als teure Aufrufe in Autorelease-Pools zu packen (wenn überhaupt möglich - meine Objekte werden möglicherweise an einen anderen Bibliothekscode gesendet). Und diese Probleme sind später schwer zu diagnostizieren, aber in erster Linie billig zu vermeiden. Wenn Sie übergebene Objekte kopieren / automatisch freigeben, können Sie verloren gehen, wenn sie viel größer sind als erwartet. Ich bin jedoch entspannter mit GUI-Code.
Eiko
@Eiko Ich bin damit einverstanden autorelease, dass der Speicher im Allgemeinen länger gehalten wird, und manuell retain/releasekann der Speicherverbrauch in diesem Fall reduziert werden. Es sollte jedoch eine Anleitung für die Optimierung von Sonderfällen sein (auch wenn Sie sich immer fühlen!), Kann nicht der Grund sein, vorzeitige Optimierung als Praxis zu verallgemeinern . Und tatsächlich ist Ihr Vorschlag nicht entgegengesetzt zu mir. Ich erwähnte es als Fall der Notwendigkeit :)
Eonil