Как мне создать делегатов в Objective-C?

Я знаю, как работают делегаты, и я знаю, как их использовать.

Но как мне их создать?

вопрос задан 9.03.2009
Andy Jacobs
7149 репутация

19 ответов


  • 856 рейтинг

    Делегат Objective C - это объект, который был назначен свойству delegate другому объекту. Чтобы создать его, вы просто определяете класс, реализующий интересующие вас методы делегата, и помечаете этот класс как реализующий протокол делегата.

    Например, предположим, у вас есть UIWebView. Если вы хотите реализовать метод его делегата webViewDidStartLoad: , вы можете создать такой класс:

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

    Затем вы можете создать экземпляр MyClass и назначить его делегатом веб-представления:

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

    На стороне UIWebView он, вероятно, имеет код, подобный этому, чтобы увидеть, отвечает ли делегат на сообщение webViewDidStartLoad:, используя respondsToSelector: , и отправьте его, если необходимо.

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

    Само свойство делегата обычно объявляется weak (в ARC) или assign (перед ARC), чтобы избежать циклов сохранения, поскольку делегат объекта часто содержит сильную ссылку на этот объект. (Например, контроллер представления часто является делегатом представления, которое он содержит. )

    Создание делегатов для ваших классов

    Чтобы определить своих собственных делегатов, вам нужно где-то объявить их методы, как описано в Документах Apple по по протоколам . Вы обычно объявляете официальный протокол. Объявление, перефразированное из UIWebView. ч, будет выглядеть так:

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

    Это аналог интерфейса или абстрактного базового класса, поскольку он создает специальный тип для вашего делегата, в данном случае UIWebViewDelegate. Делегаты-разработчики должны будут принять этот протокол:

    @interface MyClass 
    // ...
    @end
    

    А затем реализовать методы в протоколе. Для методов, объявленных в протоколе как @optional (как и большинство методов делегатов), вам необходимо проверить с -respondsToSelector: перед вызовом определенного метода для него.

    Именование

    Методы делегата обычно именуются, начиная с имени класса делегирования, и принимают делегирующий объект в качестве первого параметра. Они также часто используют волю, следует или сделали. Так, например, webViewDidStartLoad: (первый параметр - веб-представление), а не loadStarted (без параметров).

    Оптимизация скорости

    Вместо того, чтобы проверять, отвечает ли делегат на селектор каждый раз, когда мы хотим отправить его, вы можете кэшировать эту информацию, когда установлены делегаты. Один очень чистый способ сделать это - использовать битовое поле, как показано ниже:

    @protocol SomethingDelegate 
    @optional
    - (void)something:(id)something didFinishLoadingItem:(id)item;
    - (void)something:(id)something didFailWithError:(NSError *)error;
    @end
    
    @interface Something : NSObject
    @property (nonatomic, weak) id  delegate;
    @end
    
    @implementation Something {
      struct {
        unsigned int didFinishLoadingItem:1;
        unsigned int didFailWithError:1;
      } delegateRespondsTo;
    }
    @synthesize delegate;
    
    - (void)setDelegate:(id )aDelegate {
      if (delegate != aDelegate) {
        delegate = aDelegate;
    
        delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
        delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
      }
    }
    @end
    

    Затем в теле мы можем проверить, что наш делегат обрабатывает сообщения, обращаясь к нашей структуре delegateRespondsTo, а не посылая -respondsToSelector: снова и снова.

    Неофициальные делегаты

    До того, как существовали протоколы, было распространено использование категории на NSObject для объявления методов, которые может реализовать делегат. Например, CALayer все еще делает это:

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

    Это, по сути, говорит компилятору, что любой объект может реализовать displayLayer:.

    Затем вы бы использовали тот же подход -respondsToSelector:, как описано выше, чтобы вызвать этот метод. Делегаты просто реализуют этот метод и присваивают свойство delegate, и все (нет декларации о соответствии протоколу) Этот метод распространен в библиотеках Apple, но новый код должен использовать более современный протоколный подход, описанный выше, так как этот подход загрязняет NSObject (что делает автозаполнение менее полезным) и затрудняет компилятору предупреждение о опечатках и подобных ошибках.

    ответ дан Jesse Rusak, с репутацией 49088, 9.03.2009
  • 366 рейтинг

    Утвержденный ответ - это здорово, но если вы ищете 1-минутный ответ, попробуйте это:

    MyClass. h файл должен выглядеть следующим образом (добавьте строки делегатов с комментариями! )

    #import 
    
    @class MyClass;             //define class, so protocol can see MyClass
    @protocol MyClassDelegate    //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  delegate; //define MyClassDelegate as delegate
    
    @end
    

    MyClass. м файл должен выглядеть вот так

    #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
    

    Чтобы использовать ваш делегат в другом классе (в данном случае UIViewController с именем MyVC) MyVC. ч:

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

    MyVC. м:

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

    Реализовать метод делегата

    - (void) myClassDelegateMethod: (MyClass *) sender {
        NSLog(@"Delegates are great!");
    }
    
    ответ дан Tibidabo, с репутацией 19073, 30.09.2012
  • 18 рейтинг

    При использовании метода формального протокола для создания поддержки делегатов я обнаружил, что вы можете обеспечить правильную проверку типов (пусть и во время выполнения, а не во время компиляции), добавив что-то вроде:

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

    в вашем коде доступа делегата (setDelegate). Это помогает минимизировать ошибки.

    ответ дан umop, с репутацией 1393, 4.05.2010
  • 17 рейтинг

    Может быть, это больше похоже на то, что вам не хватает:

    Если вы исходите из точки зрения, подобной C ++, делегатам нужно немного привыкнуть, но в основном «они просто работают».

    Как это работает, вы устанавливаете некоторый объект, который вы написали как делегат NSWindow, но ваш объект имеет только реализации (методы) для одного или нескольких из множества возможных методов делегата. Итак, что-то происходит, и NSWindow хочет вызвать ваш объект - он просто использует метод respondsToSelector Objective-c, чтобы определить, хочет ли ваш объект вызвать этот метод, а затем вызывает его. Вот как работает target-c - методы ищутся по требованию.

    Совершенно тривиально сделать это с вашими собственными объектами, ничего особенного не происходит, например, вы можете иметь NSArray из 27 объектов, всех видов объектов, только 18 из них имеют метод -(void)setToBue;. «т. Чтобы позвонить setToBlue на все 18, которые нуждаются в этом, что-то вроде этого:

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

    Еще одна особенность делегатов в том, что они не сохраняются, поэтому вам всегда нужно установить для делегата значение nil в вашем методе MyClass dealloc.

    ответ дан Tom Andersen, с репутацией 5495, 10.03.2009
  • 17 рейтинг

    Пожалуйста! посмотрите ниже простое пошаговое руководство, чтобы понять, как делегаты работают в iOS.

    Делегат в iOS

    Я создал два ViewController (для отправки данных от одного к другому)

    1. FirstViewController реализует делегат (который предоставляет данные).
    2. SecondViewController объявляет делегата (который будет получать данные).
    ответ дан swiftBoy, с репутацией 28510, 27.02.2013
  • 15 рейтинг

    В соответствии с рекомендациями Apple, рекомендуется делегату (по определению протоколу) соответствовать протоколу NSObject.

    @protocol MyDelegate 
        ...
    @end
    

    & amp; создать дополнительные методы в вашем делегате (т.е. е. методы, которые не обязательно должны быть реализованы), вы можете использовать аннотацию @optional, как это:

    @protocol MyDelegate 
        ...
        ...
          // 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
    

    Таким образом, при использовании методов, которые вы указали как необязательные, вам необходимо (в вашем классе) проверить с помощью respondsToSelector, действительно ли представление (соответствующее вашему делегату) действительно реализовало ваш дополнительный метод (ы) или нет.

    ответ дан Jean, с репутацией 1384, 4.08.2013
  • 10 рейтинг

    Я думаю, что все эти ответы имеют большой смысл, когда вы понимаете делегатов. Лично я пришел из страны Си / Си ++, и до этого процедурные языки, такие как Фортран и т. Д., Вот мои 2 минуты, чтобы найти похожие аналоги в парадигме Си ++.

    Если бы я должен был объяснить делегатов программисту на C ++ / Java, я бы сказал

    Что такое делегаты? Это статические указатели на классы в другом классе. Как только вы назначите указатель, вы можете вызывать функции / методы в этом классе. Следовательно, некоторые функции вашего класса «делегируются» (в мире C ++ - указатель на указатель объекта класса) другому классу.

    Что такое протоколы? Концептуально это служит аналогичным назначением для файла заголовка класса, который вы назначаете в качестве класса делегата. Протокол - это явный способ определения того, какие методы должны быть реализованы в классе, указатель которого был задан как делегат внутри класса.

    Как я могу сделать что-то подобное в C ++? Если бы вы попытались сделать это в C ++, вы должны определить указатели на классы (объекты) в определении класса, а затем связать их с другими классами, которые предоставят дополнительные функции в качестве делегатов вашему базовому классу. Но эта схема должна поддерживаться в коде и будет неуклюжей и подверженной ошибкам Цель C просто предполагает, что программисты не являются лучшими в поддержании этой дисциплины, и предоставляет ограничения компилятора для обеспечения чистой реализации.

    ответ дан DrBug, с репутацией 1219, 19.06.2013
  • 8 рейтинг

    Хорошо, это не совсем ответ на вопрос, но если вы ищете, как сделать свой собственный делегат, возможно, что-то гораздо более простое может быть лучшим ответом для вас.

    Я почти не реализую своих делегатов, потому что мне это редко нужно Я могу иметь ТОЛЬКО ОДИН делегат для объекта делегата. Так что, если вы хотите, чтобы ваш делегат для односторонней связи / передачи данных, вам гораздо лучше с уведомлениями.

    NSNotification может передавать объекты нескольким получателям, и его очень легко использовать. Это работает так:

    MyClass. м файл должен выглядеть вот так

    #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
    

    Чтобы использовать ваше уведомление в других классах: Добавить класс в качестве наблюдателя:

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

    Реализация селектора:

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

    Не забудьте удалить свой класс в качестве наблюдателя, если

    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    ответ дан Tibidabo, с репутацией 19073, 17.07.2013
  • 8 рейтинг

    скажем, у вас есть класс, который вы разработали, и вы хотите объявить свойство делегата, чтобы иметь возможность уведомлять его, когда происходит какое-то событие:

    @class myClass;
    
    @protocol myClassDelegate 
    
    -(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
    

    , поэтому вы объявляете протокол в заголовочном файле MyClass (или отдельном заголовочном файле) и объявляете обязательные / необязательные обработчики событий, которые ваш делегат должен / должен реализовать, а затем объявляете свойство в MyClass типа (id< MyClassDelegate>), что означает любую цель c класс, который соответствует протоколу MyClassDelegate, вы заметите, что свойство делегата объявлено как слабое, это очень важно для предотвращения сохранения цикла (чаще всего делегат сохраняет экземпляр MyClass, поэтому, если вы объявили делегат как сохраняющий, оба они будут сохранять друг друга, и ни один из них никогда не будет выпущен).

    вы также заметите, что методы протокола передают экземпляр MyClass делегату в качестве параметра, это лучший метод, если делегат хочет вызвать некоторые методы в экземпляре MyClass, а также помогает, когда делегат объявляет себя как MyClassDelegate нескольким экземплярам MyClass например, когда у вас есть несколько UITableView's экземпляров в вашем ViewController, и вы объявляете себя как UITableViewDelegate для всех них.

    и внутри вашего MyClass вы уведомляете делегата с объявленными событиями следующим образом:

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

    вы сначала проверяете, отвечает ли ваш делегат на метод протокола, который вы собираетесь вызвать, в случае, если делегат не реализует его, а затем приложение завершит работу (даже если метод протокола требуется).

    ответ дан m.eldehairy, с репутацией 444, 8.04.2015
  • 5 рейтинг

    Чтобы создать свой собственный делегат, сначала необходимо создать протокол и объявить необходимые методы, не реализуя. А затем внедрите этот протокол в свой класс заголовка, где вы хотите реализовать методы делегата или делегата.

    Протокол должен быть объявлен как показано ниже:

    @protocol ServiceResponceDelegate 
    
    - (void) serviceDidFailWithRequestType:(NSString*)error;
    - (void) serviceDidFinishedSucessfully:(NSString*)success;
    
    @end
    

    Это класс обслуживания, где нужно выполнить какую-то задачу. Он показывает, как определить делегата и как установить делегата. В классе реализации после того, как задача выполнена, делегат вызывает методы.

    @interface ServiceClass : NSObject
    {
    id  _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
    

    Это класс основного представления, из которого вызывается класс обслуживания путем установки делегата для себя. А также протокол реализован в классе заголовка.

    @interface viewController: UIViewController 
    {
    ServiceClass* _service;
    }
    
    - (void) go;
    
    @end
    
    @implementation viewController
    
    //
    //some methods
    //
    
    - (void) go
    {
    _service = [[ServiceClass alloc] init];
    [_service setDelegate:self];
    [_service someTask];
    }
    

    Вот и все, и, реализовав методы делегата в этом классе, управление вернется после выполнения операции / задачи.

    ответ дан Mahesh, с репутацией 628, 3.08.2015