Wie erstelle ich Delegate in Objective-C?

Antworten:

889

Ein Objective-C-Delegat ist ein Objekt, dem der delegateEigenschaft ein anderes Objekt zugewiesen wurde . Um eine zu erstellen, definieren Sie eine Klasse, die die Delegatenmethoden implementiert, an denen Sie interessiert sind, und markieren diese Klasse als Implementierung des Delegatenprotokolls.

Angenommen, Sie haben eine UIWebView. Wenn Sie die webViewDidStartLoad:Methode des Delegaten implementieren möchten , können Sie eine Klasse wie die folgende erstellen:

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

Anschließend können Sie eine Instanz von MyClass erstellen und als Delegat der Webansicht zuweisen:

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

Auf der UIWebViewSeite, hat es wahrscheinlich Code ähnlich wie diese zu sehen , ob die Delegierten reagieren auf die webViewDidStartLoad:Nachricht mit respondsToSelector:ihm gegebenenfalls und senden.

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

Die Delegate-Eigenschaft selbst wird normalerweise weak(in ARC) oder assign(vor ARC) deklariert , um Aufbewahrungsschleifen zu vermeiden, da der Delegat eines Objekts häufig einen starken Verweis auf dieses Objekt enthält. (Beispielsweise ist ein Ansichtscontroller häufig der Delegat einer darin enthaltenen Ansicht.)

Delegierte für Ihre Klassen machen

Um Ihre eigenen Delegaten zu definieren, müssen Sie ihre Methoden irgendwo deklarieren, wie in den Apple Docs zu Protokollen beschrieben . Normalerweise deklarieren Sie ein formelles Protokoll. Die von UIWebView.h umschriebene Deklaration würde folgendermaßen aussehen:

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

Dies ist analog zu einer Schnittstelle oder einer abstrakten Basisklasse, da UIWebViewDelegatein diesem Fall ein spezieller Typ für Ihren Delegaten erstellt wird. Delegierte Implementierer müssten dieses Protokoll übernehmen:

@interface MyClass <UIWebViewDelegate>
// ...
@end

Und dann implementieren Sie die Methoden im Protokoll. Bei Methoden, die im Protokoll als @optional(wie die meisten delegierten Methoden) deklariert sind , müssen Sie dies überprüfen, -respondsToSelector:bevor Sie eine bestimmte Methode aufrufen.

Benennung

Delegierungsmethoden werden normalerweise beginnend mit dem Namen der delegierenden Klasse benannt und nehmen das delegierende Objekt als ersten Parameter. Sie verwenden auch oft eine Willens-, Soll- oder Did-Form. Also webViewDidStartLoad:(erster Parameter ist die Webansicht) anstatt loadStarted(ohne Parameter) zum Beispiel.

Geschwindigkeitsoptimierungen

Anstatt jedes Mal zu überprüfen, ob ein Delegat auf einen Selektor antwortet, wenn wir ihm eine Nachricht senden möchten, können Sie diese Informationen zwischenspeichern, wenn Delegierte festgelegt werden. Eine sehr saubere Möglichkeit, dies zu tun, besteht darin, ein Bitfeld wie folgt zu verwenden:

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

Dann können wir im Hauptteil überprüfen, ob unser Delegat Nachrichten verarbeitet, indem wir auf unsere delegateRespondsToStruktur zugreifen -respondsToSelector:und nicht immer wieder senden .

Informelle Delegierte

Vor Protokolle gab, war es üblich , eine verwenden Kategorie auf , NSObjectum die Verfahren zu erklären Delegierter implementieren könnte. Zum Beispiel CALayertut dies immer noch:

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

Dies teilt dem Compiler mit, dass ein Objekt implementiert werden kann displayLayer:.

Sie würden dann den gleichen -respondsToSelector:Ansatz wie oben beschrieben verwenden, um diese Methode aufzurufen. Delegaten implementieren diese Methode und weisen die delegateEigenschaft zu, und das war's (es gibt keine Erklärung, dass Sie einem Protokoll entsprechen). Diese Methode ist in Apples Bibliotheken üblich, aber neuer Code sollte den oben beschriebenen moderneren Protokollansatz verwenden, da dieser Ansatz die Umweltverschmutzung verschmutzt NSObject(was die automatische Vervollständigung weniger nützlich macht) und es dem Compiler erschwert, Sie vor Tippfehlern und ähnlichen Fehlern zu warnen.

Jesse Rusak
quelle
Ich denke, Sie müssen den unsigned intTyp umwandeln, BOOLda der Rückgabewert von delegate respondsToSelectorvom Typ ist BOOL.
Roland
Kann der Delegat wie in C ++ für den Polymorphismus verwendet werden?
@ Dan Ja, sicher. Protokolle werden im Allgemeinen für den Polymorphismus verwendet.
Jesse Rusak
@ JesseRusak Ich denke, "JSSomethingDelegate" sollte aus Gründen der Konsistenz "SomethingDelegate" sein :)
Hans Knöchel
382

Die genehmigte Antwort ist großartig, aber wenn Sie nach einer 1-minütigen Antwort suchen, versuchen Sie Folgendes:

Die Datei MyClass.h sollte folgendermaßen aussehen (Delegatenzeilen mit Kommentaren hinzufügen!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

Die Datei MyClass.m sollte so aussehen

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

So verwenden Sie Ihren Delegaten in einer anderen Klasse (in diesem Fall UIViewController MyVC) MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MyVC.m:

myClass.delegate = self;          //set its delegate to self somewhere

Implementieren Sie die Delegate-Methode

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}
Tibidabo
quelle
4
Es ist großartig, diese Antwort als Kurzreferenz zu verwenden. Aber warum ist die Delegate-Eigenschaft in Ihrer MyClass.h als "IBOutlet" markiert?
Arno van der Meer
4
@ArnovanderMeer Guter Fang! Ich kann mich nicht erinnern warum. Ich brauche es in meinem Projekt, aber nicht in diesem Beispiel, ich habe es entfernt. thx
Tibidabo
Vielen Dank. So nett und gründlich die akzeptierte Antwort auch ist, ich lerne am besten aus einem kompakten Beispielcode. Es ist gut, zwei Antworten zu haben.
Sudo
@ Tibidabo Total hervorragend. Ich wünschte wirklich, jeder könnte solche Programmierkonzepte erklären. Ich habe im Laufe der Jahre Hunderte von Erklärungen über "Delegierte" gesehen und diese Theorie bis jetzt nie wirklich verstanden! Vielen Dank ...
Charles Robertson
5
Wo wird myClassin MyVC.m instanziiert?
Lane Rettig
18

Bei Verwendung der formalen Protokollmethode zum Erstellen der Delegatenunterstützung habe ich festgestellt, dass Sie eine ordnungsgemäße Typprüfung (allerdings Laufzeit, keine Kompilierungszeit) sicherstellen können, indem Sie Folgendes hinzufügen:

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

in Ihrem Delegate Accessor (setDelegate) Code. Dies hilft, Fehler zu minimieren.

umop
quelle
18

Bitte! In der folgenden einfachen Schritt-für-Schritt-Anleitung erfahren Sie, wie Delegates in iOS funktioniert.

In iOS delegieren

Ich habe zwei ViewController erstellt (zum Senden von Daten von einem zum anderen)

  1. FirstViewController-Implementierungsdelegat (der Daten bereitstellt).
  2. SecondViewController deklariert den Delegaten (der Daten empfängt).
swiftBoy
quelle
17

Vielleicht entspricht dies eher dem, was Sie vermissen:

Wenn Sie aus einer C ++ - Perspektive kommen, sind die Delegierten etwas gewöhnungsbedürftig - aber im Grunde funktionieren sie einfach.

So funktioniert es: Sie legen ein Objekt fest, das Sie als Delegat an NSWindow geschrieben haben, aber Ihr Objekt verfügt nur über Implementierungen (Methoden) für eine oder einige der vielen möglichen Delegatmethoden. Es passiert also etwas und Sie NSWindowmöchten Ihr Objekt aufrufen. Es verwendet lediglich die respondsToSelectorMethode von Objective-c, um zu bestimmen, ob Ihr Objekt diese Methode aufrufen möchte, und ruft sie dann auf. So funktioniert Objective-C - Methoden werden bei Bedarf nachgeschlagen.

Es ist völlig trivial, dies mit Ihren eigenen Objekten zu tun, es ist nichts Besonderes los, Sie könnten zum Beispiel eines NSArrayvon 27 Objekten haben, alle verschiedene Arten von Objekten, nur 18 von ihnen haben die Methode -(void)setToBue;Die anderen 9 nicht. Also, um setToBluealle 18 anzurufen , die es tun müssen, so etwas:

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

Das andere an Delegaten ist, dass sie nicht beibehalten werden, sodass Sie den Delegaten nilin Ihrer MyClass deallocMethode immer festlegen müssen .

Tom Andersen
quelle
15

Als von Apple empfohlene bewährte Methode empfiehlt es sich, dass der Delegierte (der per Definition ein Protokoll ist) dem NSObjectProtokoll entspricht.

@protocol MyDelegate <NSObject>
    ...
@end

& Um optionale Methoden in Ihrem Delegaten zu erstellen (dh Methoden, die nicht unbedingt implementiert werden müssen), können Sie die @optionalAnnotation wie folgt verwenden:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

Wenn Sie also Methoden verwenden, die Sie als optional angegeben haben, müssen Sie (in Ihrer Klasse) überprüfen, respondsToSelectorob die Ansicht (die Ihrem Delegaten entspricht) Ihre optionalen Methoden tatsächlich implementiert hat oder nicht.

Jean
quelle
11

Ich denke, all diese Antworten sind sehr sinnvoll, wenn Sie die Delegierten verstehen. Persönlich kam ich aus dem Land von C / C ++ und davor aus prozeduralen Sprachen wie Fortran usw. Hier ist meine 2-minütige Darstellung ähnlicher Analoga im C ++ - Paradigma.

Wenn ich einem C ++ / Java-Programmierer die Delegierten erklären würde, würde ich sagen

Was sind Delegierte? Dies sind statische Zeiger auf Klassen innerhalb einer anderen Klasse. Sobald Sie einen Zeiger zugewiesen haben, können Sie Funktionen / Methoden in dieser Klasse aufrufen. Daher werden einige Funktionen Ihrer Klasse an eine andere Klasse "delegiert" (in C ++ world - Zeiger auf durch einen Klassenobjektzeiger).

Was sind Protokolle? Konzeptionell dient es einem ähnlichen Zweck wie die Header-Datei der Klasse, die Sie als Delegatenklasse zuweisen. Ein Protokoll ist eine explizite Methode, um zu definieren, welche Methoden in der Klasse implementiert werden müssen, deren Zeiger als Delegat innerhalb einer Klasse festgelegt wurde.

Wie kann ich in C ++ etwas Ähnliches tun? Wenn Sie dies in C ++ versuchen würden, würden Sie Zeiger auf Klassen (Objekte) in der Klassendefinition definieren und diese dann mit anderen Klassen verbinden, die zusätzliche Funktionen als Delegaten für Ihre Basisklasse bereitstellen. Diese Verkabelung muss jedoch im Code enthalten sein und ist ungeschickt und fehleranfällig. Ziel C geht lediglich davon aus, dass Programmierer diese Disziplin nicht am besten einhalten können, und bietet Compiler-Einschränkungen, um eine saubere Implementierung durchzusetzen.

DrBug
quelle
Sie sprechen von Semantik, während ich über die Intuition sprach. Sie sprechen von einer virtuellen Funktion - aber es kann schwierig sein, sich an die neue Terminologie zu gewöhnen. Die Antwort
richtet sich
Was Sie sagen, ist mir nicht wirklich klar. Warum schreibst du keine neue Antwort und lässt uns sehen, ob mehr Leute sie nützlich finden, sie werden darüber abstimmen?
DrBug
9

Schnelle Version

Ein Delegat ist nur eine Klasse, die für eine andere Klasse arbeitet. Lesen Sie den folgenden Code für ein etwas albernes (aber hoffentlich aufschlussreiches) Spielplatzbeispiel, das zeigt, wie dies in Swift gemacht wird.

// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
    // This protocol only defines one required method
    func getYourNiceOlderSiblingAGlassOfWater() -> String
}

class BossyBigBrother {

    // The delegate is the BossyBigBrother's slave. This position can 
    // be assigned later to whoever is available (and conforms to the 
    // protocol).
    weak var delegate: OlderSiblingDelegate?

    func tellSomebodyToGetMeSomeWater() -> String? {
        // The delegate is optional because there might not be anyone
        // nearby to boss around.
        return delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {

    // This method is repquired by the protocol, but the protocol said
    // nothing about how it needs to be implemented.
    func getYourNiceOlderSiblingAGlassOfWater() -> String {
        return "Go get it yourself!"
    }

}

// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()

// Set the delegate 
// bigBro could boss around anyone who conforms to the 
// OlderSiblingDelegate protocol, but since lilSis is here, 
// she is the unlucky choice.
bigBro.delegate = lilSis

// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
    print(replyFromLilSis) // "Go get it yourself!"
}

In der Praxis werden Delegierte häufig in den folgenden Situationen eingesetzt

  1. Wenn eine Klasse Informationen an eine andere Klasse weitergeben muss
  2. Wenn eine Klasse einer anderen Klasse erlauben möchte, sie anzupassen

Die Klassen müssen vorher nichts voneinander wissen, außer dass die Delegatenklasse dem erforderlichen Protokoll entspricht.

Ich empfehle dringend, die folgenden zwei Artikel zu lesen. Sie haben mir geholfen, die Delegierten noch besser zu verstehen als die Dokumentation .

Suragch
quelle
8

Ok, dies ist nicht wirklich eine Antwort auf die Frage, aber wenn Sie nachschlagen, wie Sie Ihren eigenen Delegierten bilden können, könnte etwas viel Einfacheres eine bessere Antwort für Sie sein.

Ich setze meine Delegierten kaum um, weil ich sie selten brauche. Ich kann NUR EINEN Delegierten für ein Delegatenobjekt haben. Wenn Sie also möchten, dass Ihr Delegierter in eine Richtung kommuniziert / Daten weitergibt, sind Sie mit Benachrichtigungen viel besser dran.

NSNotification kann Objekte an mehr als einen Empfänger übergeben und ist sehr einfach zu verwenden. Es funktioniert so:

Die Datei MyClass.m sollte so aussehen

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

So verwenden Sie Ihre Benachrichtigung in anderen Klassen: Fügen Sie eine Klasse als Beobachter hinzu:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

Implementieren Sie den Selektor:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

Vergessen Sie nicht, Ihre Klasse als Beobachter zu entfernen, wenn

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Tibidabo
quelle
8

Angenommen, Sie haben eine Klasse, die Sie entwickelt haben, und möchten eine Delegate-Eigenschaft deklarieren, um sie benachrichtigen zu können, wenn ein Ereignis eintritt:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

Sie deklarieren also ein Protokoll in der MyClassHeader-Datei (oder einer separaten Header-Datei) und deklarieren die erforderlichen / optionalen Ereignishandler, die Ihr Delegat implementieren muss / sollte, und deklarieren dann eine Eigenschaft MyClassvom Typ ( id< MyClassDelegate>), was bedeutet, dass jede objektive c-Klasse konform ist Im Protokoll MyClassDelegatewerden Sie feststellen, dass die Delegate-Eigenschaft als schwach deklariert ist. Dies ist sehr wichtig, um den Aufbewahrungszyklus zu verhindern (meistens behält der Delegate dieMyClass Wenn Sie Instanz bei. Wenn Sie den Delegaten als Retain deklariert haben, behalten sich beide gegenseitig und keine von ihnen werden jemals veröffentlicht werden).

Sie werden auch feststellen, dass die Protokollmethoden die MyClassInstanz als Parameter an den Delegaten übergeben. Dies ist eine bewährte Methode für den Fall, dass der Delegat einige Methoden für eine MyClassInstanz aufrufen möchte, und hilft auch, wenn sich der Delegat MyClassDelegatefür mehrere MyClassInstanzen deklariert , z. B. wenn Sie mehrere haben UITableView'sInstanzen in Ihrem ViewControllerund erklärt sich als UITableViewDelegatezu allen von ihnen.

und in Ihrem MyClassbenachrichtigen Sie den Delegierten mit deklarierten Ereignissen wie folgt:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

Sie überprüfen zunächst, ob Ihr Delegat auf die Protokollmethode reagiert, die Sie aufrufen möchten, falls der Delegat sie nicht implementiert und die App dann abstürzt (auch wenn die Protokollmethode erforderlich ist).

m.eldehairy
quelle
6

Hier ist eine einfache Methode zum Erstellen von Delegaten

Protokoll in .h-Datei erstellen. Stellen Sie sicher, dass dies vor dem Protokoll mit @class gefolgt vom Namen des UIViewControllers definiert ist< As the protocol I am going to use is UIViewController class>.

Schritt: 1: Erstellen Sie ein neues Klassenprotokoll mit dem Namen "YourViewController", das die Unterklasse der UIViewController-Klasse ist, und weisen Sie diese Klasse dem zweiten ViewController zu.

Schritt: 2: Gehen Sie zur Datei "YourViewController" und ändern Sie sie wie folgt:

#import <UIKit/UIkit.h>
@class YourViewController;

@protocol YourViewController Delegate <NSObject>

 @optional
-(void)defineDelegateMethodName: (YourViewController *) controller;

@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;

  @end
  @interface YourViewController : UIViewController

  //Since the property for the protocol could be of any class, then it will be marked as a type of id.

  @property (nonatomic, weak) id< YourViewController Delegate> delegate;

@end

Die im Protokollverhalten definierten Methoden können mit @optional und @required als Teil der Protokolldefinition gesteuert werden.

Schritt: 3: Implementierung des Delegierten

    #import "delegate.h"

   @interface YourDelegateUser ()
     <YourViewControllerDelegate>
   @end

   @implementation YourDelegateUser

   - (void) variousFoo {
      YourViewController *controller = [[YourViewController alloc] init];
      controller.delegate = self;
   }

   -(void)defineDelegateMethodName: (YourViewController *) controller {
      // handle the delegate being called here
   }

   -(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
      // handle the delegate being called here
      return YES;
   }

   @end

// teste, ob die Methode definiert wurde, bevor du sie aufrufst

 - (void) someMethodToCallDelegate {
     if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
           [self.delegate delegateMethodName:self]; 
     }
  }
Soumya
quelle
5

Um Ihren eigenen Delegaten zu erstellen, müssen Sie zunächst ein Protokoll erstellen und die erforderlichen Methoden deklarieren, ohne sie zu implementieren. Implementieren Sie dieses Protokoll dann in Ihre Header-Klasse, in der Sie den Delegaten oder die Delegatmethoden implementieren möchten.

Ein Protokoll muss wie folgt deklariert werden:

@protocol ServiceResponceDelegate <NSObject>

- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;

@end

Dies ist die Serviceklasse, in der eine Aufgabe ausgeführt werden sollte. Es zeigt, wie Sie einen Delegaten definieren und wie Sie den Delegaten festlegen. In der Implementierungsklasse werden nach Abschluss der Aufgabe die Methoden des Delegaten aufgerufen.

@interface ServiceClass : NSObject
{
id <ServiceResponceDelegate> _delegate;
}

- (void) setDelegate:(id)delegate;
- (void) someTask;

@end

@implementation ServiceClass

- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}

- (void) someTask
{
/*

   perform task

*/
if (!success)
{
[_delegate serviceDidFailWithRequestType:@”task failed”];
}
else
{
[_delegate serviceDidFinishedSucessfully:@”task success”];
}
}
@end

Dies ist die Hauptansichtsklasse, von der aus die Serviceklasse aufgerufen wird, indem der Delegat auf sich selbst gesetzt wird. Außerdem ist das Protokoll in der Header-Klasse implementiert.

@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}

- (void) go;

@end

@implementation viewController

//
//some methods
//

- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}

Das war's, und durch die Implementierung von Delegate-Methoden in dieser Klasse wird die Steuerung wieder hergestellt, sobald die Operation / Aufgabe abgeschlossen ist.

Mahesh
quelle
4

Haftungsausschluss: Dies ist die SwiftVersion zum Erstellen eines delegate.

Was sind Delegierte? … In der Softwareentwicklung gibt es allgemeine wiederverwendbare Lösungsarchitekturen, die dazu beitragen, häufig auftretende Probleme in einem bestimmten Kontext zu lösen. Diese „Vorlagen“ werden sozusagen am besten als Entwurfsmuster bezeichnet. Delegaten sind ein Entwurfsmuster, mit dem ein Objekt Nachrichten an ein anderes Objekt senden kann, wenn ein bestimmtes Ereignis eintritt. Stellen Sie sich vor, ein Objekt A ruft ein Objekt B auf, um eine Aktion auszuführen. Sobald die Aktion abgeschlossen ist, sollte Objekt A wissen, dass B die Aufgabe abgeschlossen hat, und die erforderlichen Maßnahmen ergreifen. Dies kann mit Hilfe von Delegierten erreicht werden!

Zur besseren Erklärung zeige ich Ihnen, wie Sie mit Swift in einer einfachen Anwendung einen benutzerdefinierten Delegaten erstellen, der Daten zwischen Klassen weitergibt. Laden Sie zunächst dieses Starterprojekt herunter oder klonen Sie es und führen Sie es aus!

Sie können eine App mit zwei Klassen sehen, ViewController Aund ViewController B. B hat zwei Ansichten, die beim Tippen die Hintergrundfarbe von ändern ViewController, nichts zu kompliziert, oder? Nun wollen wir auf einfache Weise darüber nachdenken, auch die Hintergrundfarbe von Klasse A zu ändern, wenn auf die Ansichten von Klasse B getippt wird.

Das Problem ist, dass diese Ansichten Teil der Klasse B sind und keine Ahnung von Klasse A haben. Daher müssen wir einen Weg finden, um zwischen diesen beiden Klassen zu kommunizieren, und hier erstrahlt die Delegation. Ich habe die Implementierung in 6 Schritte unterteilt, damit Sie diese bei Bedarf als Spickzettel verwenden können.

Schritt 1: Suchen Sie in der ClassBVC-Datei nach der Pragma-Markierung Schritt 1 und fügen Sie diese hinzu

//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}

Der erste Schritt besteht darin, ein protocolProtokoll zu erstellen . In diesem Fall erstellen wir das Protokoll in Klasse B. Innerhalb des Protokolls können Sie so viele Funktionen erstellen, wie Sie möchten, basierend auf den Anforderungen Ihrer Implementierung. In diesem Fall haben wir nur eine einfache Funktion, die ein optionales UIColorArgument akzeptiert . delegateIn diesem Fall empfiehlt es sich, Ihre Protokolle zu benennen, indem Sie das Wort am Ende des Klassennamens hinzufügen ClassBVCDelegate.

Schritt 2: Suchen Sie nach der Pragma-Markierung in Schritt 2 ClassVBCund fügen Sie diese hinzu

//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?

Hier erstellen wir nur eine Delegate-Eigenschaft für die Klasse. Diese Eigenschaft muss den protocolTyp übernehmen und sollte optional sein. Außerdem sollten Sie das schwache Schlüsselwort vor der Eigenschaft hinzufügen, um Speicherzyklen und potenzielle Speicherverluste zu vermeiden. Wenn Sie nicht wissen, was dies bedeutet, machen Sie sich vorerst keine Sorgen. Denken Sie daran, dieses Schlüsselwort hinzuzufügen.

Schritt 3: Suchen Sie nach der Pragma-Markierung Schritt 3 im handleTap methodin ClassBVCund fügen Sie diese hinzu

//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)

Eine Sache, die Sie wissen sollten, führen Sie die App aus und tippen Sie auf eine Ansicht. Sie werden kein neues Verhalten sehen und das ist richtig, aber ich möchte darauf hinweisen, dass die App nicht abstürzt, wenn der Delegat aufgerufen wird, und Das liegt daran, dass wir es als optionalen Wert erstellen und es deshalb nicht abstürzt, selbst wenn der Delegierte noch nicht existiert. Gehen wir jetzt zur ClassAVCAkte und machen es, der Delegierte.

Schritt 4: Suchen Sie in der handleTap-Methode in nach der Pragma-Markierung Schritt 4 ClassAVC und fügen Sie diese wie neben Ihren Klassentyp ein.

//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}

Jetzt hat ClassAVC die ClassBVCDelegate Protokoll übernommen hat, können Sie sehen, dass Ihr Compiler Ihnen einen Fehler mit der Meldung "Typ" ClassAVC entspricht nicht dem Protokoll "ClassBVCDelegate" anzeigt. Dies bedeutet nur, dass Sie die Methoden des Protokolls noch nicht verwendet haben. Stellen Sie sich das vor Wenn Klasse A das Protokoll annimmt, ist das wie das Unterzeichnen eines Vertrags mit Klasse B und dieser Vertrag besagt: "Jede Klasse, die mich adoptiert, MUSS meine Funktionen nutzen!"

Kurzer Hinweis: Wenn Sie aus einem Objective-CHintergrund stammen, denken Sie wahrscheinlich, dass Sie diesen Fehler auch beenden können, indem Sie diese Methode optional machen. Zu meiner und wahrscheinlich Ihrer Überraschung wird die SwiftSprache jedoch nicht optional protocolsunterstützt. Wenn Sie dies möchten, können Sie sie erstellen eine Erweiterung für Ihreprotocol oder verwenden Sie das Schlüsselwort @objc in Ihrer protocolImplementierung.

Persönlich, wenn ich ein Protokoll mit verschiedenen optionalen Methoden erstellen muss, würde ich es vorziehen, es in verschiedene zu unterteilen protocols. Auf diese Weise folge ich dem Konzept, meinen Objekten eine einzige Verantwortung zu übertragen, aber es kann je nach spezifischer Implementierung variieren.

hier ist ein guter Artikel über optionale Methoden.

Schritt 5: Suchen Sie nach der Pragma-Markierung Schritt 5 in der Methode zur Vorbereitung auf den Übergang und fügen Sie diese hinzu

//MARK: step 5 create a reference of Class B and bind them through the `prepareforsegue` method.
if let nav = segue.destination as? UINavigationController, let classBVC = nav.topViewController as? ClassBVC {
classBVC.delegate = self
}

Hier erstellen wir nur eine Instanz von ClassBVCund weisen ihren Delegierten dem Selbst zu, aber was ist das Selbst hier? Nun, Selbst ist das, ClassAVCwas delegiert wurde!

Schritt 6: Suchen Sie abschließend nach dem Pragma in Schritt 6 ClassAVCund verwenden Sie die Funktionen von protocol, beginnen Sie mit der Eingabe von func changeBackgroundColor, und Sie werden sehen, dass es automatisch für Sie vervollständigt wird. Sie können eine beliebige Implementierung hinzufügen. In diesem Beispiel ändern wir nur die Hintergrundfarbe und fügen diese hinzu.

//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}

Führen Sie jetzt die App aus!

Delegatessind überall und Sie verwenden sie wahrscheinlich ohne Vorankündigung. Wenn Sie eine tableviewin der Vergangenheit verwendete Delegation erstellen , lösen viele Klassen von UIKITArbeiten um sie herum und viele andere frameworksdiese Hauptprobleme.

  • Vermeiden Sie eine enge Kopplung von Gegenständen.
  • Ändern Sie Verhalten und Erscheinungsbild, ohne dass Objekte in Unterklassen unterteilt werden müssen.
  • Ermöglichen, dass Aufgaben für ein beliebiges Objekt ausgeführt werden.

Herzlichen Glückwunsch, Sie implementieren gerade einen benutzerdefinierten Delegaten. Ich weiß, dass Sie wahrscheinlich denken, so viel Ärger nur dafür? Nun, Delegation ist ein sehr wichtiges Entwurfsmuster, um zu verstehen, ob Sie ein werden möchteniOS Entwickler werden , und denken Sie immer daran, dass sie eine Eins-zu-Eins-Beziehung zwischen Objekten haben.

Das Original-Tutorial finden Sie hier

James Rochabrun
quelle
4

Die Antwort wird tatsächlich beantwortet, aber ich möchte Ihnen einen "Spickzettel" zum Erstellen eines Delegaten geben:

DELEGATE SCRIPT

CLASS A - Where delegate is calling function

@protocol <#Protocol Name#> <NSObject>

-(void)delegateMethod;

@end

@interface <#Some ViewController#> : <#UIViewController#> 

@property (nonatomic, assign) id <<#Protocol Name#>> delegate;

@end


@implementation <#Some ViewController#> 

-(void)someMethod {
    [self.delegate methodName];
}

@end




CLASS B - Where delegate is called 

@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end

@implementation <#Other ViewController#> 

-(void)otherMethod {
    CLASSA *classA = [[CLASSA alloc] init];

    [classA setDelegate:self];
}

-delegateMethod() {

}

@end
Miras Maratuly
quelle
2

ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

ViewController viewController = [ViewController new];
viewController.delegate = self;

Methode:

-(void)delegateMEthod: (ArgType) arg{
}
Lal Krishna
quelle
2

Aus meiner Sicht erstellen Sie eine separate Klasse für diese Delegatmethode, und Sie können sie verwenden, wo Sie möchten.

in meiner benutzerdefinierten DropDownClass.h

typedef enum
{
 DDSTATE,
 DDCITY
}DropDownType;

@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString     DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
 BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;

danach in.m Datei Array mit Objekten erstellen,

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

if (self.delegate) {
    if (self.dropDownType == DDCITY) {
        cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
    }
    else if (self.dropDownType == DDSTATE) {
        cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
    }
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 [self dismissViewControllerAnimated:YES completion:^{
    if(self.delegate){
        if(self.dropDownType == DDCITY){
            [self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
        else if (self.dropDownType == DDSTATE) {
            [self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
    }
}];
}

Hier sind alle für die benutzerdefinierte Delegatenklasse festgelegt. Danach können Sie diese Delegatenmethode verwenden, wo Sie möchten. Zum Beispiel ...

in meinem anderen viewcontroller danach importieren

Erstellen Sie eine Aktion zum Aufrufen dieser Delegatenmethode

- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}

danach Aufruf delegate Methode wie folgt

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}
User558
quelle
0

Delegieren: - Erstellen

@protocol addToCartDelegate <NSObject>

-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;

@end

Senden Sie und weisen Sie den Delegierten zu, um anzuzeigen, dass Sie Daten senden

[self.delegate addToCartAction:itemsModel isAdded:YES];
Vaibhav Gaikwad
quelle
0
//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

//2.
//Create a weak reference in a class where you declared the delegate
@property(weak,nonatomic)id <TB_RemovedUserCellTag> removedCellTagDelegate;

//3. 
// use it in the class
  [self.removedCellTagDelegate didRemoveCellWithTag:self.tag];

//4. import the header file in the class where you want to conform to the protocol
@interface MyClassUsesDelegate ()<TB_RemovedUserCellTag>

@end

// 5. Implementieren Sie die Methode in der Klasse .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);

}}

Rohit Kashyap
quelle
0

Beginnen wir mit einem Beispiel: Wenn wir ein Produkt online kaufen, durchläuft es einen Prozess wie Versand / Lieferung, der von verschiedenen Teams abgewickelt wird. Wenn der Versand abgeschlossen ist, sollte das Versandteam das Lieferteam benachrichtigen und es sollte eine Eins-zu-Eins-Kommunikation sein, um diese Informationen zu senden Dies wäre ein Overhead für andere Personen / Anbieter, die diese Informationen möglicherweise nur an erforderliche Personen weitergeben möchten.

Wenn wir also in Bezug auf unsere App denken, kann eine Veranstaltung eine Online-Bestellung sein und verschiedene Teams können wie mehrere Ansichten sein.

Hier ist der Code, der ShippingView als Versandteam und DeliveryView als Lieferteam betrachtet:

//Declare the protocol with functions having info which needs to be communicated
protocol ShippingDelegate : class {
    func productShipped(productID : String)
}
//shippingView which shows shipping status of products
class ShippingView : UIView
{

    weak var delegate:ShippingDelegate?
    var productID : String

    @IBAction func checkShippingStatus(sender: UIButton)
    {
        // if product is shipped
        delegate?.productShipped(productID: productID)
    }
}
//Delivery view which shows delivery status & tracking info
class DeliveryView: UIView,ShippingDelegate
{
    func productShipped(productID : String)
    {
        // update status on view & perform delivery
    }
}

//Main page on app which has both views & shows updated info on product whole status
class ProductViewController : UIViewController
{
    var shippingView : ShippingView
    var deliveryView : DeliveryView

    override func viewDidLoad() {
        super.viewDidLoad()
        // as we want to update shipping info on delivery view, so assign delegate to delivery object
        // whenever shipping status gets updated it will call productShipped method in DeliveryView & update UI.
        shippingView.delegate = deliveryView
        //
    }
}
Ellen
quelle