iOS/Objective-C: как смешивать делегирование и блоки?

У меня два класса. Delegator использует делегирование для отправки своего результата. Blocker использует блоки в статических методах.

Без изменения Delegator, как я могу элегантно и легко реализовать methodWithBlock, чтобы блок вызывался с результатом, полученным methodWithDelegate?

Делегатор:

@class Delegator;

@protocol Delegate <NSObject>
- (void)delegator:(Delegator *)sender producedResult:(int)result;
@end

@interface Delegator : NSObject
@property (weak, nonatomic) id <Delegate> delegate;
- (void)methodWithDelegate;
@end

@implementation Delegator
- (void)methodWithDelegate
{
    // Some asynchronous computation resulting in
    [self.delegate delegator:self producedResult:42];
}
@end

Блокировщик:

@interface Blocker : NSObject
+ (void)methodWithBlock:(void(^)(int result))block;
@end

@implementation Blocker
+ (void)methodWithBlock:(void(^)(int result))block
{
    // How to call Delegator's methodWithDelegate and return
    // the result using block ?
    ...
}
@end

Исследуемые решения:

  • Оберните Delegator в новый класс или категорию и создайте метод, возвращающий блок, как это предлагается в этот ответ. Эти решения работают, но слишком сложны и требуют много времени.

  • Заставьте Blocker соответствовать протоколу Delegate и сохраните блок в свойстве, создайте его экземпляр в методе methodWithBlock и вызовите блок при вызове метода делегирования. Это не работает, так как нет надежного указателя на этот новый экземпляр, и он уничтожается.

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


person Julien Dupuis    schedule 30.01.2013    source источник
comment
Не уверен, что в этом случае можно избежать осложнений. Я бы выбрал номер 3, если он реализован правильно, он должен стать победителем. Не бойтесь сложных вещей :)   -  person Stavash    schedule 30.01.2013
comment
Не совсем понятно, какой результат вы возвращаете из -methodWithDelegate, поскольку все в вашем примере кода возвращает void.   -  person Jonathan Grynspan    schedule 30.01.2013
comment
Я не совсем уверен, что понимаю вопрос, но я уверен, что мог бы помочь вам, если бы понял. Blocker использует статические функции, которые вычисляют вещи, а затем отправляют результат через блок. Вы вызываете эти функции следующим образом: [Blocker methodWithBlock:^(int result) { ... делать что угодно с результатом ... }];, в чем проблема? Какую ссылку вы теряете?   -  person Ismael    schedule 30.01.2013
comment
@Ismael: я пытаюсь реализовать метод methodWithBlock.   -  person Julien Dupuis    schedule 01.02.2013


Ответы (2)


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

person Fruity Geek    schedule 30.01.2013
comment
да. Apple, кажется, движется в этом направлении, чтобы сообщать о состоянии в таких объектах, как NSOperationQueue, и резервировать делегирование, когда вы действительно хотите вмешаться и изменить/расширить функциональность без создания подклассов. - person Fruity Geek; 31.01.2013

Элегантное и простое решение — просто добавить блочный метод к вашему Delegator (например, через категорию). Я знаю, вы сказали, что не можете этого сделать, но мы должны признать, что любое другое решение на основе блоков, в котором вы создаете новые классы для достижения желаемого эффекта, не проходит тест на элегантность.

Сказав это, если вам отчаянно нужны блочные методы и вы не можете расширить Delegator, то я бы склонялся к варианту 2, за исключением того, что вместо того, чтобы создавать эти методы класса, сделать их методами экземпляра и Blocker создать экземпляр Delegator и определить методы-оболочки, чтобы классы, которые использовали бы Delegator, теперь могут использовать Blocker, но с блоками completion, а не с делегированными обратными вызовами. И класс, который ранее создавал экземпляр Delegator, теперь может вместо этого создавать экземпляр Blocker.

person Rob    schedule 30.01.2013