Als Neuling bei Objective-C, Kakao und iPhone-Entwicklern im Allgemeinen habe ich den starken Wunsch, das Beste aus der Sprache und den Frameworks herauszuholen.
Eine der Ressourcen, die ich verwende, sind die CS193P-Klassennotizen von Stanford, die sie im Web hinterlassen haben. Es enthält Vorlesungsunterlagen, Aufgaben und Beispielcode, und da der Kurs von Apple-Entwicklern gehalten wurde, halte ich ihn definitiv für "aus dem Maul des Pferdes".
Klassen-Website:
http://www.stanford.edu/class/cs193p/cgi-bin/index.php
Vorlesung 08 bezieht sich auf eine Aufgabe zum Erstellen einer UINavigationController-basierten App, bei der mehrere UIViewController auf den UINavigationController-Stapel übertragen werden. So funktioniert der UINavigationController. Das ist logisch. Die Folie enthält jedoch einige strenge Warnungen bezüglich der Kommunikation zwischen Ihren UIViewControllern.
Ich werde aus dieser ernsthaften Folie zitieren:
http://cs193p.stanford.edu/downloads/08-NavigationTabBarControllers.pdf
Seite 16/51:
So teilen Sie keine Daten
- Globale Variablen oder Singletons
- Dies schließt Ihren Anwendungsdelegierten ein
- Direkte Abhängigkeiten machen Ihren Code weniger wiederverwendbar
- Und schwieriger zu debuggen und zu testen
OK. Ich bin damit fertig. Werfen Sie nicht blind alle Ihre Methoden, die für die Kommunikation zwischen dem Viewcontroller verwendet werden, in Ihren App-Delegaten und verweisen Sie auf die Viewcontroller-Instanzen in den App-Delegat-Methoden. Fair 'nuff.
Ein Stück weiter, bekommen wir diese Folie uns sagen , was wir sollten tun.
Seite 18/51:
Best Practices für den Datenfluss
- Finden Sie genau heraus , was kommuniziert werden muss
- Definieren Sie Eingabeparameter für Ihren View Controller
- Verwenden Sie für die Kommunikation zur Sicherung der Hierarchie eine lose Kopplung
- Definieren Sie eine generische Schnittstelle für Beobachter (wie Delegierung).
Auf diese Folie folgt dann eine scheinbar Platzhalterfolie, auf der der Dozent anscheinend anhand eines Beispiels mit dem UIImagePickerController die Best Practices demonstriert. Ich wünschte, die Videos wären verfügbar! :((
Ok, also ... ich fürchte, mein Objekt ist nicht so stark. Ich bin auch ein bisschen verwirrt von der letzten Zeile im obigen Zitat. Ich habe ziemlich viel darüber gegoogelt und einen anständigen Artikel gefunden, der über die verschiedenen Methoden der Beobachtungs- / Benachrichtigungstechniken spricht:
http://cocoawithlove.com/2008/06/five-approaches-to -listening-Observing.html
Methode 5 zeigt sogar Delegierte als Methode an! Außer .... Objekte können jeweils nur einen Delegaten festlegen. Was soll ich also tun, wenn ich mehrere Viewcontroller-Kommunikation habe?
Ok, das ist die aufgebaute Bande. Ich weiß, dass ich meine Kommunikationsmethoden im App-Delegaten leicht ausführen kann, indem ich auf die mehreren Viewcontroller-Instanzen in meinem Appdelegate verweise, aber ich möchte so etwas richtig machen.
Bitte helfen Sie mir, "das Richtige zu tun", indem Sie die folgenden Fragen beantworten:
- Wenn ich versuche, einen neuen Viewcontroller auf dem UINavigationController-Stack zu pushen, wer sollte diesen Push ausführen? Welche Klasse / Datei in meinem Code ist der richtige Ort?
- Was ist der "richtige" Weg, wenn ich Daten (Wert eines iVar) in einem meiner UIViewController beeinflussen möchte, wenn ich mich in einem anderen UIViewController befinde?
- Geben Sie an, dass in einem Objekt jeweils nur ein Delegat festgelegt werden kann. Wie würde die Implementierung aussehen, wenn der Dozent sagt: "Definieren Sie eine generische Schnittstelle für Beobachter (wie Delegation)" . Ein Pseudocode-Beispiel wäre hier nach Möglichkeit sehr hilfreich.
quelle
Antworten:
Dies sind gute Fragen, und es ist großartig zu sehen, dass Sie diese Forschung betreiben und sich anscheinend darum kümmern, zu lernen, wie man "es richtig macht", anstatt es nur zusammen zu hacken.
Erstens stimme ich den vorherigen Antworten zu, die sich darauf konzentrieren, wie wichtig es ist, Daten gegebenenfalls in Modellobjekte einzufügen (gemäß dem MVC-Entwurfsmuster). Normalerweise möchten Sie vermeiden, Statusinformationen in einen Controller einzufügen, es sei denn, es handelt sich ausschließlich um "Präsentations" -Daten.
Zweitens finden Sie auf Seite 10 der Stanford-Präsentation ein Beispiel für das programmgesteuerte Verschieben eines Controllers auf den Navigationscontroller. Ein Beispiel dafür, wie dies mit Interface Builder "visuell" ausgeführt wird, finden Sie in diesem Lernprogramm .
Drittens und vielleicht am wichtigsten ist zu beachten, dass die in der Stanford-Präsentation erwähnten "Best Practices" viel einfacher zu verstehen sind, wenn Sie sie im Kontext des Entwurfsmusters "Abhängigkeitsinjektion" betrachten. Kurz gesagt bedeutet dies, dass Ihr Controller die Objekte, die er für seine Arbeit benötigt, nicht "nachschlagen" sollte (z. B. auf eine globale Variable verweisen). Stattdessen sollten Sie diese Abhängigkeiten immer in den Controller "einfügen" (dh die benötigten Objekte über Methoden übergeben).
Wenn Sie dem Abhängigkeitsinjektionsmuster folgen, ist Ihr Controller modular und wiederverwendbar. Und wenn Sie darüber nachdenken, woher die Stanford-Moderatoren kommen (dh als Apple-Mitarbeiter müssen sie Klassen erstellen, die leicht wiederverwendet werden können), haben Wiederverwendbarkeit und Modularität hohe Priorität. Alle Best Practices, die sie für den Datenaustausch erwähnen, sind Teil der Abhängigkeitsinjektion.
Das ist der Kern meiner Antwort. Im Folgenden wird ein Beispiel für die Verwendung des Abhängigkeitsinjektionsmusters mit einem Controller aufgeführt, falls dies hilfreich ist.
Beispiel für die Verwendung der Abhängigkeitsinjektion mit einem View Controller
Angenommen, Sie erstellen einen Bildschirm, in dem mehrere Bücher aufgelistet sind. Der Benutzer kann Bücher auswählen, die er kaufen möchte, und dann auf die Schaltfläche "Kasse" tippen, um zum Kassenbildschirm zu gelangen.
Um dies zu erstellen, können Sie eine BookPickerViewController-Klasse erstellen, die die GUI- / Ansichtsobjekte steuert und anzeigt. Woher bekommt es alle Buchdaten? Angenommen, dies hängt von einem BookWarehouse-Objekt ab. Jetzt vermittelt Ihr Controller im Grunde genommen Daten zwischen einem Modellobjekt (BookWarehouse) und den GUI- / Ansichtsobjekten. Mit anderen Worten, BookPickerViewController hängt vom BookWarehouse-Objekt ab.
Tu das nicht:
Stattdessen sollten die Abhängigkeiten wie folgt eingefügt werden:
Wenn die Apple-Leute über die Verwendung des Delegierungsmusters sprechen, um "die Hierarchie zu sichern", sprechen sie immer noch über die Abhängigkeitsinjektion. Was sollte der BookPickerViewController in diesem Beispiel tun, wenn der Benutzer seine Bücher ausgewählt hat und zum Auschecken bereit ist? Nun, das ist nicht wirklich seine Aufgabe. Es sollte diese Arbeit auf ein anderes Objekt DELEGIEREN, was bedeutet, dass es von einem anderen Objekt abhängt. Daher können wir unsere BookPickerViewController-Init-Methode wie folgt ändern:
Das Nettoergebnis all dessen ist, dass Sie mir Ihre BookPickerViewController-Klasse (und verwandte GUI- / Ansichtsobjekte) geben können und ich sie problemlos in meiner eigenen Anwendung verwenden kann, vorausgesetzt, BookWarehouse und CheckoutController sind generische Schnittstellen (dh Protokolle), die ich implementieren kann ::
Schließlich ist Ihr BookPickerController nicht nur wiederverwendbar, sondern auch einfacher zu testen.
quelle
So etwas ist immer Geschmackssache.
Trotzdem ziehe ich es immer vor, meine Koordination (# 2) über Modellobjekte durchzuführen. Der View Controller der obersten Ebene lädt oder erstellt die benötigten Modelle, und jeder View Controller legt Eigenschaften in seinen untergeordneten Controllern fest, um ihnen mitzuteilen, mit welchen Modellobjekten sie arbeiten müssen. Die meisten Änderungen werden mithilfe von NSNotificationCenter in der Hierarchie gesichert. Das Auslösen der Benachrichtigungen ist normalerweise in das Modell selbst integriert.
Angenommen, ich habe eine App mit Konten und Transaktionen. Ich habe auch einen AccountListController, einen AccountController (der eine Kontoübersicht mit der Schaltfläche "Alle Transaktionen anzeigen" anzeigt), einen TransactionListController und einen TransactionController. AccountListController lädt eine Liste aller Konten und zeigt sie an. Wenn Sie auf ein Listenelement tippen, wird die Eigenschaft .account des AccountControllers festgelegt und der AccountController auf den Stapel verschoben. Wenn Sie auf die Schaltfläche "Alle Transaktionen anzeigen" tippen, lädt AccountController die Transaktionsliste, fügt sie in die .transactions-Eigenschaft von TransactionListController ein und verschiebt den TransactionListController auf den Stapel usw.
Wenn beispielsweise TransactionController die Transaktion bearbeitet, nimmt er die Änderung in seinem Transaktionsobjekt vor und ruft dann seine 'save'-Methode auf. 'save' sendet eine TransactionChangedNotification. Jeder andere Controller, der sich selbst aktualisieren muss, wenn sich die Transaktion ändert, würde die Benachrichtigung beobachten und sich selbst aktualisieren. TransactionListController würde vermutlich; AccountController und AccountListController können je nachdem, was sie versucht haben.
Für # 1 hatte ich in meinen frühen Apps eine Art displayModel: withNavigationController: -Methode im untergeordneten Controller, die Dinge einrichtete und den Controller auf den Stapel schob. Aber als ich mich mit dem SDK wohler gefühlt habe, bin ich davon abgewichen, und jetzt lassen mich normalerweise die Eltern das Kind schieben.
Betrachten Sie für # 3 dieses Beispiel. Hier verwenden wir zwei Controller, AmountEditor und TextEditor, um zwei Eigenschaften einer Transaktion zu bearbeiten. Die Editoren sollten die zu bearbeitende Transaktion nicht speichern, da der Benutzer entscheiden könnte, die Transaktion abzubrechen. Stattdessen nehmen beide ihren übergeordneten Controller als Delegaten und rufen eine Methode auf, die angibt, ob sie etwas geändert haben.
Und jetzt ein paar Methoden von TransactionController:
Zu beachten ist, dass wir ein generisches Protokoll definiert haben, mit dem Redakteure mit ihrem eigenen Controller kommunizieren können. Auf diese Weise können wir die Editoren in einem anderen Teil der Anwendung wiederverwenden. (Möglicherweise können Konten auch Notizen enthalten.) Natürlich kann das EditorDelegate-Protokoll mehr als eine Methode enthalten. In diesem Fall ist dies der einzige notwendige Fall.
quelle
Editor.delegate
Mitglied. In meinerviewDidLoad
Methode bekomme ichProperty 'delegate' not found...
. Ich bin mir nur nicht sicher, ob ich etwas anderes vermasselt habe. Oder wenn dies der Kürze halber gekürzt ist.Ich sehe dein Problem ..
Was passiert ist, ist, dass jemand die Idee der MVC-Architektur verwirrt hat.
MVC besteht aus drei Teilen. Modelle, Ansichten und Controller. Das angegebene Problem scheint zwei davon ohne guten Grund kombiniert zu haben. Ansichten und Controller sind separate Logikelemente.
Also ... Sie möchten nicht mehrere View-Controller haben.
Sie möchten mehrere Ansichten und einen Controller haben, der zwischen ihnen wählt. (Sie könnten auch mehrere Controller haben, wenn Sie mehrere Anwendungen haben)
Ansichten sollten KEINE Entscheidungen treffen. Die Controller sollten dies tun. Daher die Trennung von Aufgaben, Logik und Möglichkeiten, Ihnen das Leben zu erleichtern.
Also ... stellen Sie sicher, dass Ihre Ansicht genau das tut und eine schöne Ansicht der Daten liefert. Lassen Sie Ihren Controller entscheiden, was mit den Daten geschehen soll und welche Ansicht verwendet werden soll.
(und wenn wir über Daten sprechen, sprechen wir über das Modell ... eine nette Standardmethode zum Storring, Zugriff, Modifizieren ... eine weitere separate Logik, die wir wegpacken und vergessen können)
quelle
Angenommen, es gibt zwei Klassen A und B.
Instanz der Klasse A ist
Eine Instanz;
Klasse A macht und Instanz von Klasse B, als
B Instanz;
Und in Ihrer Logik der Klasse B müssen Sie irgendwo eine Methode der Klasse A kommunizieren oder auslösen.
1) Falscher Weg
Sie können die Instanz an die Instanz übergeben. Platzieren Sie nun den Aufruf der gewünschten Methode [aInstance methodname] von der gewünschten Stelle in bInstance.
Dies hätte Ihren Zweck erfüllt, aber während der Veröffentlichung hätte ein Speicher gesperrt und nicht freigegeben.
Wie?
Wenn Sie die aInstance an bInstance übergeben haben, haben wir den Retaincount von aInstance um 1 erhöht. Bei der Freigabe von bInstance wird der Speicher blockiert, da aInstance niemals durch bInstance auf 0 Retaincount gebracht werden kann, da bInstance selbst ein Objekt von aInstance ist.
Da aInstance stecken bleibt, bleibt auch der Speicher von bInstance hängen (durchgesickert). Selbst wenn aInstance zu einem späteren Zeitpunkt freigegeben wird, wird auch sein Speicher blockiert, da bInstance nicht freigegeben werden kann und bInstance eine Klassenvariable von aInstance ist.
2) Richtiger Weg
Durch die Definition von aInstance als Delegat von bInstance kommt es nicht zu einer Änderung des Retaincount oder einer Speicherverschränkung von aInstance.
bInstance kann die in aInstance liegenden Delegatenmethoden frei aufrufen. Bei der Freigabe von bInstance werden alle Variablen selbst erstellt und freigegeben. Bei der Freigabe von aInstance werden sie sauber freigegeben, da aInstance nicht in bInstance verwickelt ist.
quelle