Jak utworzyć delegatów w Objective-C?

Wiem, jak działają delegaci i wiem, jak mogę ich użyć.

Ale jak je stworzyć?

Author: Wayne Chen, 2009-03-09

19 answers

Delegat Objective-C jest obiektem, który został przypisany do właściwości delegate inny obiekt. Aby go utworzyć, po prostu zdefiniuj klasę, która implementuje metody delegatów, którymi jesteś zainteresowany, i zaznacz tę klasę jako implementującą protokół delegatów.

Na przykład, załóżmy, że masz UIWebView. Jeśli chcesz wdrożyć jego delegata webViewDidStartLoad: metoda, Można utworzyć klasę taką jak ta:

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

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

Następnie można utworzyć instancję MyClass i przypisać to jako delegat widoku sieci:

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

Po stronie UIWebView prawdopodobnie ma kod podobny do tego, aby sprawdzić, czy delegat odpowiada na wiadomość webViewDidStartLoad: za pomocą respondsToSelector: i wysłać go, jeśli to konieczne.

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

Sama właściwość delegate jest zazwyczaj deklarowana weak (W ARC) lub assign (pre-ARC), aby uniknąć pętli zachowywania, ponieważ delegat obiektu często zawiera silne odniesienie do tego obiektu. (Na przykład kontroler widoku jest często delegatem widoku it zawiera.)

Tworzenie delegatów na twoje zajęcia

Aby zdefiniować własnych delegatów, musisz gdzieś zadeklarować ich metody, jak zostało to omówione w } dokumentach Apple dotyczących protokołów . Zazwyczaj deklarujesz oficjalny protokół. Deklaracja, sparafrazowana z UIWebView.h, wyglądałoby tak:

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

Jest to analogiczne do interfejsu lub abstrakcyjnej klasy bazowej, ponieważ tworzy specjalny typ dla Twojego delegata, UIWebViewDelegate w tym przypadku. Realizatorzy delegatów musieliby przyjmuje Niniejszy protokół:

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

A następnie zaimplementować metody w protokole. Dla metod zadeklarowanych w protokole jako @optional (jak większość metod delegowanych), przed wywołaniem określonej metody należy sprawdzić za pomocą -respondsToSelector:.

Nazewnictwo

Metody delegowania są zwykle nazwane zaczynając od nazwy klasy delegującej i przyjmują obiekt delegujący jako pierwszy parametr. Często używają też formy will -, should-lub did-form. Tak więc webViewDidStartLoad: (pierwszy parametr to web widok) zamiast loadStarted (nie biorąc żadnych parametrów) na przykład.

Optymalizacja Prędkości

Zamiast sprawdzać, czy delegat odpowiada na selektor za każdym razem, gdy chcemy go wysłać, możesz buforować te informacje, gdy delegaty są ustawione. Jednym z bardzo czystych sposobów na to jest użycie bitfielda, w następujący sposób: {]}

@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

Następnie, w ciele, możemy sprawdzić, czy nasz delegat obsługuje wiadomości poprzez dostęp do naszej struktury delegateRespondsTo, a nie wysyłając -respondsToSelector: w kółko jeszcze raz.

Nieformalni Delegaci

Zanim istniały protokoły, powszechne było używanie kategorii na NSObject do deklarowania metod, które delegat może zaimplementować. Na przykład, CALayer nadal robi to:

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

To zasadniczo mówi kompilatorowi, że każdy obiekt może zaimplementować displayLayer:.

Do wywołania tej metody należy użyć tego samego podejścia -respondsToSelector:, jak opisano powyżej. Delegaci po prostu implementują tę metodę i przypisują właściwość delegate i to wszystko (nie ma deklaracji zgodności z protokołem). Ta metoda jest powszechna w bibliotekach Apple, ale nowy kod powinien używać bardziej nowoczesnego podejścia do protokołu, ponieważ to podejście zanieczyszcza NSObject (co sprawia, że autouzupełnianie jest mniej przydatne) i utrudnia kompilatorowi ostrzeganie o literówkach i podobnych błędach.

 856
Author: Jesse Rusak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-25 20:02:47

Zatwierdzona odpowiedź jest świetna, ale jeśli szukasz 1-minutowej odpowiedzi, spróbuj tego:

MyClass.plik h powinien wyglądać tak (Dodaj wiersze delegatów z komentarzami!)
#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
MyClass.plik m powinien wyglądać tak
#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

Aby użyć delegata w innej klasie (UIViewController o nazwie MyVC w tym przypadku) 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

Zaimplementuj metodę delegata

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}
 366
Author: Tibidabo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 13:40:41

Podczas korzystania z formalnej metody protokołu do tworzenia obsługi delegatów, odkryłem, że można zapewnić prawidłowe sprawdzanie typu (chociaż, runtime, a nie czas kompilacji), dodając coś w stylu:

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

W kodzie delegata (setDelegate). Pomaga to zminimalizować błędy.

 18
Author: umop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-04 20:42:59

Może to jest bardziej podobne do tego, co brakuje:

Jeśli przychodzisz z punktu widzenia C++, delegaci trochę się przyzwyczajają - ale w zasadzie "po prostu działają".

Sposób w jaki to działa polega na tym, że ustawiasz jakiś obiekt, który napisałeś jako delegat do NSWindow, ale twój obiekt ma tylko implementacje (metody) dla jednej lub kilku z wielu możliwych metod delegatów. Więc coś się dzieje i NSWindow chce wywołać Twój obiekt - po prostu używa Objective-c respondsToSelector metoda aby określić, czy obiekt chce wywołać tę metodę, a następnie ją wywołać. Tak działa objective-c - metody są sprawdzane na żądanie.

Jest to całkowicie trywialne, aby to zrobić z własnymi obiektami, nie ma nic specjalnego się dzieje, możesz na przykład mieć NSArray z 27 obiektów, wszystkich różnych rodzajów obiektów, tylko 18 niektóre z nich mają metodę -(void)setToBue; Inne 9 nie. więc aby wywołać setToBlue na wszystkich 18, które potrzebują tego zrobić, coś takiego: {9]}

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

The other rzecz w tym, że delegaci nie są zachowywani, więc zawsze musisz ustawić delegata na nil w swojej metodzie MyClass dealloc.

 17
Author: Tom Andersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-20 17:42:20

Proszę! Sprawdź poniżej prosty samouczek krok po kroku, aby zrozumieć, jak delegaci działają w systemie iOS.

Deleguj w iOS

Utworzyłem dwa Viewcontrollery (do przesyłania danych z jednego do drugiego)

  1. FirstViewController implementuje delegata (który dostarcza dane).
  2. SecondViewController deklaruje delegata (który otrzyma dane).
 17
Author: swiftBoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-04 06:34:23

Jako dobra praktyka zalecana przez Apple, dobrze jest, aby delegat (który z definicji jest protokołem), był zgodny z protokołem NSObject.

@protocol MyDelegate <NSObject>
    ...
@end

& aby utworzyć Opcjonalne metody wewnątrz twojego delegata (tj. metody, które niekoniecznie muszą być zaimplementowane), możesz użyć adnotacji @optional w następujący sposób:

@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

Więc używając metod, które określiłeś jako opcjonalne, musisz (w swojej klasie) sprawdzić za pomocą respondsToSelector Czy widok (który jest zgodny z Twoim delegatem) rzeczywiście zaimplementowano Opcjonalne metody lub nie.

 15
Author: Jean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 12:28:43

Myślę, że wszystkie te odpowiedzi mają sens, gdy zrozumiesz delegatów. Osobiście pochodzę z krainy C/C++, a wcześniej języków proceduralnych, takich jak Fortran itp., Więc oto moje 2 min podejście do znajdowania podobnych analogów w paradygmacie C++.

Gdybym miał wytłumaczyć programiście C++ / Java to powiedziałbym

Czym są delegaci ? Są to statyczne wskaźniki do klas w ramach innej klasy. Po przypisaniu wskaźnika możesz wywołać funkcje / metody w tej klasie. Stąd niektóre funkcje klasy są "delegowane" (w C++ world - pointer to by a class object pointer) do innej klasy.

Co to są protokoły ? Koncepcyjnie służy ona do podobnego celu jak plik nagłówkowy klasy, którą przypisujesz jako klasę delegata. Protokół jest jawnym sposobem definiowania, jakie metody muszą być zaimplementowane w klasie, który wskaźnik został ustawiony jako delegat w klasie.

Jak mogę zrobić coś podobnego w C++? Jeśli próbowałeś to zrobić w C++, można by zdefiniować wskaźniki do klas (obiektów) w definicji klasy, a następnie połączyć je z innymi klasami, które zapewnią dodatkowe funkcje jako delegatów do klasy bazowej. Ale to okablowanie musi być utrzymane w kodzie i będzie niezdarne i podatne na błędy. Objective C zakłada tylko, że programiści nie są najlepsi w utrzymywaniu tej linii i zapewnia ograniczenia kompilatora, aby wymusić czystą implementację.

 10
Author: DrBug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 10:34:42

Wersja Swift

Delegat to tylko klasa, która wykonuje jakąś pracę dla innej klasy. Przeczytaj poniższy kod, aby uzyskać nieco głupi (ale miejmy nadzieję pouczający) przykład placu zabaw, który pokazuje, jak to się robi w Swift.

// 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!"
}

W praktyce delegaci są często używane w następujących sytuacjach

  1. gdy klasa musi przekazać pewne informacje innej klasie
  2. gdy Klasa chce zezwolić innej klasie na jej dostosowanie

The klasy nie muszą wiedzieć nic o sobie wcześniej, z wyjątkiem tego, że Klasa delegata jest zgodna z wymaganym protokołem.

Gorąco polecam przeczytanie następujących dwóch artykułów. Pomogły mi zrozumieć delegatów jeszcze lepiej niż dokumentacja .

 9
Author: Suragch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-02 14:45:08

Ok, to nie jest tak naprawdę odpowiedź na pytanie, ale jeśli szukasz, Jak zrobić własnego delegata, może coś znacznie prostszego mogłoby być lepszą odpowiedzią dla Ciebie.

Nie realizuję swoich delegatów, bo rzadko ich potrzebuję. Mogę mieć tylko jednego delegata dla obiektu delegata. Więc jeśli chcesz, aby twój delegat do komunikacji/przekazywania danych w jedną stronę był znacznie lepszy od powiadomień.

NSNotification może przekazywać obiekty do więcej niż jednego odbiorcy i jest bardzo łatwy w użyciu. Działa tak:

MyClass.plik m powinien wyglądać tak
#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

Aby użyć Twojego powiadomienia w innych klasach: Dodaj klasę jako obserwatora:

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

Zaimplementuj 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
}

Nie zapomnij usunąć swojej klasy jako obserwatora, Jeśli

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
 8
Author: Tibidabo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-16 23:23:13

Powiedzmy, że masz klasę, którą stworzyłeś i chcesz zadeklarować właściwość delegata, aby móc powiadomić ją, gdy wydarzy się jakieś zdarzenie:

@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

Więc deklarujesz protokół w pliku nagłówkowym MyClass (lub oddzielnym pliku nagłówkowym) i deklarujesz wymagane / opcjonalne procedury obsługi zdarzeń, które twój delegat musi/powinien zaimplementować , a następnie deklarujesz właściwość w MyClass typu (id< MyClassDelegate>), co oznacza dowolną klasę objective c, która jest zgodna z protokołem MyClassDelegate, zauważysz, że właściwość delegate jest zadeklarowana jako słaba, jest to bardzo ważne, aby zapobiec cyklowi zachowywania (najczęściej delegat zachowuje instancję MyClass, więc jeśli zadeklarowałeś delegata jako zachowaj, obie zachowają się nawzajem i żadna z nich nigdy nie zostanie zwolniona).

Zauważysz również, że metody protokołu przekazują instancję MyClass do delegata jako parametru , jest to najlepsza praktyka w przypadku, gdy delegat chce wywołać niektóre metody na instancji MyClass, a także pomaga, gdy delegat deklaruje się jako MyClassDelegate do wielu instancji MyClass, Jak wtedy, gdy masz wiele instancji UITableView's w swoim ViewController i deklaruje się jako UITableViewDelegate do wszystkich z nich.

I wewnątrz MyClass powiadamiasz delegata o zadeklarowanych zdarzeniach w następujący sposób:

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

Najpierw sprawdzasz, czy twój delegat reaguje na metodę protokołu, którą chcesz wywołać, na wypadek, gdyby delegat jej nie zaimplementował, a aplikacja ulegnie awarii (nawet jeśli wymagana jest metoda protokołu).

 8
Author: m.eldehairy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 10:23:13

Oto prosta metoda tworzenia delegatów

Utwórz protokół w .plik H. Upewnij się, że jest zdefiniowany przed protokołem używając @ class, po którym następuje nazwa UIViewController < As the protocol I am going to use is UIViewController class>.

Krok: 1: Utwórz nowy protokół klasy o nazwie "YourViewController", który będzie podklasą klasy UIViewController i przypisz tę klasę do drugiego Viewcontrollera.

Krok: 2: przejdź do pliku "YourViewController" i zmodyfikuj go jako poniżej:

#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

Metody zdefiniowane w zachowaniu protokołu mogą być kontrolowane za pomocą @optional i @required jako część definicji protokołu.

Krok: 3: implementacja delegata

    #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

//sprawdź, czy metoda została zdefiniowana, zanim ją wywołasz

 - (void) someMethodToCallDelegate {
     if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
           [self.delegate delegateMethodName:self]; 
     }
  }
 6
Author: Sujania,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-25 12:57:40

Aby utworzyć własnego delegata, najpierw musisz utworzyć protokół i zadeklarować niezbędne metody, bez implementacji. A następnie zaimplementuj ten protokół do swojej klasy nagłówka, w której chcesz zaimplementować metody delegate lub delegate.

Protokół musi być zadeklarowany jak poniżej:

@protocol ServiceResponceDelegate <NSObject>

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

@end

Jest to klasa usług, w której należy wykonać jakieś zadanie. Pokazuje, jak zdefiniować delegata i jak ustawić delegata. W klasie implementacji po wykonaniu zadania metody delegata są wywoływane.

@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

Jest to główna klasa widoku, z której Klasa service jest wywoływana przez ustawienie delegata do siebie. A także protokół jest zaimplementowany w klasie nagłówka.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

To wszystko, a implementując metody delegatów w tej klasie, Kontrola powróci po wykonaniu operacji/zadania.

 5
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-02 17:12:52

Disclaimer: to jest Swift Wersja jak utworzyć delegate.

Czym są delegaci? ...w rozwoju oprogramowania istnieją ogólne architektury rozwiązań wielokrotnego użytku, które pomagają rozwiązać powszechnie występujące problemy w danym kontekście, te "szablony", że tak powiem, są najlepiej znane jako wzorce projektowe. Delegacje to wzorzec projektowy, który pozwala jednemu obiektowi wysyłać wiadomości do innego obiektu, gdy ma miejsce określone zdarzenie. Wyobraź sobie, że obiekt a wywołuje obiekt B, aby wykonać akcja. Po zakończeniu akcji obiekt A powinien wiedzieć, że B wykonał zadanie i podjąć niezbędne działania, można to osiągnąć przy pomocy delegatów!

Aby uzyskać lepsze wyjaśnienie, pokażę Ci, jak utworzyć niestandardowy delegat, który przekazuje dane między klasami, za pomocą Swift w prostej aplikacji, zacznij od pobrania lub klonowania tego projektu startowego i uruchom go!

Możesz zobaczyć aplikację z dwoma klasami, ViewController A i ViewController B. B ma dwa widoki, które NA tap zmienia kolor tła ViewController, nic zbyt skomplikowanego prawda? dobrze teraz pomyślmy w łatwy sposób, aby również zmienić kolor tła klasy A, gdy widoki na klasie B są tapped.

Problem polega na tym, że te poglądy są częścią klasy B i nie mają pojęcia o klasie A, więc musimy znaleźć sposób na komunikację między tymi dwiema klasami, i to jest miejsce, w którym delegacja świeci. Podzieliłem implementację na 6 kroków, dzięki czemu możesz użyć tego jako ściągawki, gdy potrzebujesz to.

Krok 1: poszukaj znaku pragma Krok 1 w pliku ClassBVC i dodaj to

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

Pierwszym krokiem jest utworzenie protocol, w tym przypadku utworzymy protokół w klasie B, wewnątrz protokołu możesz utworzyć tyle funkcji, ile chcesz w oparciu o wymagania Twojej implementacji. W tym przypadku mamy tylko jedną prostą funkcję, która przyjmuje opcjonalny UIColor jako argument. Jest dobrą praktyką, aby nazwać swoje protokoły dodając słowo delegate na końcu klasy nazwa, w tym przypadku ClassBVCDelegate.

Krok 2: Poszukaj znaku pragmy Krok 2 w ClassVBC i dodaj to

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

Tutaj po prostu tworzymy właściwość delegata dla klasy, ta właściwość musi przyjąć typ protocol i powinna być opcjonalna. Ponadto, należy dodać słabe słowo kluczowe przed właściwością, aby uniknąć zatrzymywania cykli i potencjalnych wycieków pamięci, jeśli nie wiesz, co to oznacza, nie martw się na razie, po prostu pamiętaj, aby dodać to słowo kluczowe.

Krok 3: poszukaj znaku pragmy krok 3 wewnątrz uchwytu method W ClassBVC i dodaj to

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

Jedna rzecz, którą powinieneś wiedzieć, uruchom aplikację i dotknij dowolnego widoku, nie zobaczysz żadnego nowego zachowania i to jest poprawne, ale rzeczą, którą chcę podkreślić, jest to, że aplikacja nie zawiesza się, gdy delegat jest wywoływany, i to dlatego, że tworzymy go jako wartość opcjonalną i dlatego nie zawiesi się nawet delegowany jeszcze nie istnieje. Przejdźmy teraz do pliku ClassAVC i zrób to, delegowany.

Krok 4: poszukaj pragma zaznacz Krok 4 wewnątrz metody handleTap w ClassAVC i dodaj to obok swojego typu klasy w ten sposób.

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

Teraz ClassAVC przyjęło protokół ClassBVCDelegate, widać, że Twój kompilator wyświetla błąd, który mówi: "Typ 'ClassAVC nie jest zgodny z protokołem 'ClassBVCDelegate' i to tylko oznacza, że nie używałeś jeszcze metod protokołu, wyobraź sobie, że kiedy Klasa A przyjmuje protokół jest jak podpisanie umowy z klasą B i ta umowa mówi: "każda klasa przyjmująca protokół muszę korzystać z moich funkcji!"

Szybka uwaga: jeśli pochodzisz z tła Objective-C, prawdopodobnie myślisz, że możesz również zamknąć ten błąd czyniąc tę metodę opcjonalną, ale ku mojemu zdziwieniu, a prawdopodobnie Twojej, język Swift nie obsługuje opcjonalnej protocols, Jeśli chcesz to zrobić, możesz utworzyć rozszerzenie dla swojej protocol lub użyć słowa kluczowego @objc w swojej implementacji protocol.

Osobiście, jeśli muszę stworzyć protokół z różnymi opcjonalnymi metodami, wolałbym aby podzielić ją na inną protocols, w ten sposób będę podążał za koncepcją powierzenia jednej odpowiedzialności moim przedmiotom, ale może się ona różnić w zależności od konkretnej implementacji.

Oto dobry artykuł o metodach opcjonalnych.

Krok 5: poszukaj znaku pragmy krok 5 w metodzie prepare for segue i dodaj to
//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
}

Tutaj właśnie tworzymy instancję ClassBVC i przypisujemy jej delegata do jaźni, ale czym jest jaźń tutaj? cóż, self is the ClassAVC która została przekazana!

Krok 6: na koniec poszukaj kroku pragma 6 w ClassAVC i użyjmy funkcji protocol, zacznij wpisywać func changeBackgroundColor , a zobaczysz, że jest to automatyczne uzupełnianie go dla Ciebie. Możesz dodać dowolną implementację wewnątrz niego, w tym przykładzie po prostu zmienimy kolor tła, dodaj to.

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

Teraz uruchom aplikację!

Delegates są wszędzie i prawdopodobnie używasz ich bez zauważenia, jeśli stworzysz tableview w przeszłość, której używałeś, wiele klas UIKIT działa wokół nich i wiele innych frameworks również rozwiązują te główne problemy.

  • unikaj ciasnego łączenia obiektów.
  • Modyfikuj zachowanie i wygląd bez konieczności podklasowania obiektów.
  • pozwala na przenoszenie zadań do dowolnego dowolnego obiektu.

Gratulacje, właśnie wdrażasz niestandardowego delegata, wiem, że pewnie myślisz, tyle kłopotów tylko z tego powodu? cóż, delegacja jest bardzo ważny wzorzec projektowy, aby zrozumieć, jeśli chcesz zostać programistą {36]} i zawsze pamiętaj, że mają one jeden do jednego związek między obiektami.

Możesz zobaczyć oryginalny samouczek tutaj
 3
Author: James Rochabrun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-23 11:00:22

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;

Metoda:

-(void)delegateMEthod: (ArgType) arg{
}
 2
Author: Lal Krishna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 04:14:58

Z mojego punktu widzenia Utwórz osobną klasę dla tej metody delegata i możesz jej używać tam, gdzie chcesz.

W moim Custom 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;

Potem w.m plik tworzy tablicę z obiektami,

 - (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];
        }
    }
}];
}

Tutaj wszystkie są ustawione dla niestandardowej klasy delegata.następnie możesz użyć tej metody delegowania tam, gdzie chcesz.na przykład...

In my another ViewController import after that

Utwórz akcję do wywołania metody delegata w ten sposób

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

Po tym wywołanie metody delegata w ten sposób

- (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;
}
}
 1
Author: Mangi Reddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-08 09:30:59

Delegate: - Create

@protocol addToCartDelegate <NSObject>

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

@end

Wyślij i przypisz delegata, aby wyświetlić wysyłane DANE

[self.delegate addToCartAction:itemsModel isAdded:YES];
 0
Author: Vaibhav Gaikwad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-08 10:50:58
//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. Zaimplementuj metodę w klasie .na - (void)didRemoveCellWithTag: (NSInteger)tag { NSLog@("Tag % d", tag);

}

 0
Author: Rohit Kashyap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-02 16:31:44

Zacznijmy od przykładu, jeśli kupujemy produkt online, przechodzi on proces podobny do wysyłki / dostawy obsługiwanej przez różne teams.So jeśli wysyłka zostanie zakończona, zespół wysyłkowy powinien powiadomić zespół dostaw i powinna to być komunikacja jeden do jednego, ponieważ nadawanie tych informacji byłoby narzutowe dla innych osób / sprzedawca może chcieć przekazać te informacje tylko wymaganym osobom.

Więc jeśli myślimy w kategoriach naszej aplikacji, wydarzenie może być zamówieniem online i różne zespoły mogą być jak wiele widoków.

Oto kod rozważ ShippingView jako zespół wysyłki i Deliview jako zespół dostawy:

//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
        //
    }
}
 0
Author: Ellen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-14 10:14:49

Odpowiedź jest rzeczywiście odpowiedział, ale chciałbym dać ci "Ściągawka" do tworzenia delegata:

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
 0
Author: Miras Maratuly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 04:40:48