2017-02-10 35 views
5

Я не уверен, что это можно сделать, или, если это даже unrecommended.Внедрение UITableViewDataSource протокол в двух различных классов

То, что я пытаюсь достичь следующего:

У меня есть 2 классов classA и classB, которые имеют ссылку на тот же UITableview инстанции. То, что я хочу, чтобы classA заботиться о реализации 2 необходимых методов протокола UITableViewDataSource:

  • numberOfRowsInSection
  • cellForRowAt

Тогда я хочу classB, чтобы быть в состоянии осуществить другой необязательные методы, например, titleForHeaderInSection.

Так как может classA использовать по умолчанию некоторые методы протокола, и пусть classB будет классом, который может быть построен поверх того, что сделал classB?

В некотором смысле, проблема, с которой я столкнулся заключается в следующем: как несколько классов могут быть DataSource одного UITableView?

EDIT: classA будет в библиотеке, что я пишу, что берет на себя строительство основных частей Tableview. classB будет использоваться сторонним разработчиком, чтобы в основном настроить его внешний вид.

+1

Легко, либо 'classA' перенаправляет некоторые вызовы на' classB', либо вы создадите 'classC', который будет делегатом, и перенаправит либо на' A', и на 'B'. – Sulthan

+0

@ Султан я вижу. Я хочу сделать все доступные методы 'UITableViewDataSource'' classB', и было бы неплохо избежать написания всех методов один за другим, чтобы перенаправить. Итак, из того, что я понимаю, вариант 1 не будет работать для меня. И то же самое для варианта 2, так как мне придется писать много кода. Есть что-то, чего я не хватает? (возможно, есть простой способ перенаправить то, что мне не хватает). Также отредактировал вопрос с еще одной информацией. –

+0

Если ClassA существует исключительно для целей внедрения значений по умолчанию, вы можете позаботиться об этом быстрее, используя расширение протокола. https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521 –

ответ

3

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

protocol MyTableViewProtocol : UITableViewDelegate, UITableViewDataSource { 

} 

extension MyTableViewProtocol { 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return 5 
    } 
} 

А затем сделать ClassB реализовать MyTableViewProtocol вместо UITableViewDelegate и UITableViewDataSource.

Однако такое решение не будет работать, потому что расширения протокола недоступны Obj-C.

Я думаю, что моющее средство (и работы) решение было бы создать реализацию numberOfRowsInSection и cellForRowAt вне протокола и просто позволить ClassB называть их внутри метода делегата, например:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return MyTable.tableView(tableView: tableView, numberOfRowsInSection: section) 
} 

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

Конечно, классическое решение, чтобы определить свой собственный делегат:

protocol MyTableViewProtocol { 
    func myTableView(_ tableView: MyTableView, ...) 
    ...  
} 

и перенаправлять все к нему от делегата.

Это решение не позволяет ClassB перезаписать функцию делегата, которую вы не хотите перезаписывать.

+0

Отличный ответ, особенно часть «clearer для пользователя». Я думаю, что это будет тот, который я буду использовать. Посмотрите, есть ли еще предложения. Однако мне интересно, если вы предложите использовать вышеупомянутое решение или вручную перенаправить его? (потому что я пошел с ручным перенаправлением сейчас, и он работает отлично. Но недостатком является то, что есть много шаблонов кода, и некоторая магия происходит за спиной пользователя) –

+1

@GuyDaher Я не думаю, что перенаправление является идеальным но я использовал его несколько раз, особенно в ситуациях, когда у меня также есть подклассовое табличное представление, поэтому я могу создать лучший делегат, который уже использует мой подклассов. Однако это всегда было для внутреннего использования в проекте, а не для открытой библиотеки. – Sulthan

0

Вы можете сделать вам что-то вроде этого.Люди, которые пишут class B, используют extension A для добавления UITableViewDataSource функций.

// file A.swift 
class A:NSObject, UITableViewDataSource { 
    var b:B! = nil 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return 0 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = UITableViewCell() 
     return cell 
    } 
} 

protocol SectionNameProtocol { 
    var sectionName:[String] { get set } 
} 

// file B.swift 

class B:SectionNameProtocol { 
    unowned var a:A 
    var sectionName: [String] = [] 

    init(a:A) { 
     self.a = a 
     a.b = self 
    } 
} 

extension A { 
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 
     return b.sectionName[section] 
    } 
} 
+1

Это хороший ответ спасибо! мое предпочтение - ответ @Sulthan, поскольку он больше подходит для архитектуры, которую я хочу. –

+0

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

+0

@Sulthan Я не видел, что в вопросе было много требований к реализации. Так что это будет плюс, но не требование. Кроме того, мой ответ может ограничивать класс B от реализации функций, таких как 'func tableView (_ tableView: UITableView, numberOfRowsInSection раздел: Int)'. Но ваш ответ на расширение протокола не может. Ваш ответ зависит от людей, которые реализуют в классе B, чтобы следовать правилам, что не очень хорошая идея, чем моя. –

3

Мой ответ состоит из двух частей. В первой части я хотел бы обсудить ваше дизайнерское решение, а во втором - еще одно альтернативное решение с использованием магии Obj-C.

Конструктивные соображения

Похоже, что вы хотите ClassB, чтобы не быть в состоянии переопределить реализацию по умолчанию.

Прежде всего, в таком случае вы, вероятно, следует также применять

optional public func numberOfSections(in tableView: UITableView) -> Int 

в вашем ClassA для последовательности или ClassB будет иметь возможность вернуть что-то еще там без возможности возврата дополнительных ячеек.

На самом деле это неподобающее поведение - это то, что мне не нравится в таком дизайне. Что делать, если пользователь вашей библиотеки хочет добавить больше разделов и ячеек к тому же UITableView? В этом аспекте дизайн, описанный Sulthan с ClassA, обеспечивающий реализацию по умолчанию и ClassB, обертывает его для делегирования и, вероятно, иногда меняет значения по умолчанию, представляется мне предпочтительным. Я имею в виду что-то вроде

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    if (section == 0) { 
     return libTableDataSource.tableView(tableView: tableView, numberOfRowsInSection: section) 
    } 
    else { 
     // custom logic for additional sections 
    } 
} 

Также такая конструкция имеет еще одно преимущество, не нуждаясь продвинутые трюки Obj-C для работы в более сложных сценариев, таких как UITableViewDelegate, потому что вы не должны реализовать дополнительные методы, которые вы не хотите в либо ClassA, либо ClassB, и вы можете добавить методы, которые вам (пользователю библиотеки) нужны в ClassB.

Obj-C магия

Предположим, что вы все еще хотите, чтобы ваше поведение по умолчанию, чтобы стоять как единственно возможный выбор для методов вы реализованы, но давайте настроить другие методы. Предположим также, что мы имеем дело с чем-то вроде UITableView, который разработан с использованием метода Obj-C, т. Е. В значительной степени полагается на необязательные методы в делегатах и ​​не предоставляет простого способа назвать стандартное поведение Apple (это неверно для UITableViewDataSource, но верно для UITableViewDelegate потому что, кто знает, как реализовать что-то вроде

optional public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 

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

Так в чем же решение? Используя немного магии Obj-C, мы можем создать наш класс, который будет иметь наши реализации по умолчанию для методов протокола, которые мы хотим, чтобы, если мы предоставим ему еще один делегат с несколькими дополнительными методами, наш объект будет выглядеть так, как будто он имеет их слишком.

Попытка # 1 (NSProxy)

Сначала мы начнем с общим SOMulticastProxy, который вроде прокси, который делегаты призывает к двум объектам (см источников хелперов SOOptionallyRetainHolder далее).

SOMulticastProxy.ч

@interface SOMulticastProxy : NSProxy 

+ (id)proxyForProtocol:(Protocol *)targetProtocol firstDelegateR:(id <NSObject>)firstDelegate secondDelegateNR:(id <NSObject>)secondDelegate; 

// This provides sensible defaults for retaining: typically firstDelegate will be created in 
// place and thus should be retained while the second delegate most probably will be something 
// like UIViewController and retaining it will retaining it will lead to memory leaks 
+ (id)proxyForProtocol:(Protocol *)targetProtocol firstDelegate:(id <NSObject>)firstDelegate retainFirst:(BOOL)retainFirst 
     secondDelegate:(id <NSObject>)secondDelegate retainSecond:(BOOL)retainSecond; 
@end 

SOMulticastProxy.m

@interface SOMulticastProxy() 
@property(nonatomic) Protocol *targetProtocol; 
@property(nonatomic) NSArray<SOOptionallyRetainHolder *> *delegates; 

@end 

@implementation SOMulticastProxy { 
} 

- (id)initWithProtocol:(Protocol *)targetProtocol firstDelegate:(id <NSObject>)firstDelegate retainFirst:(BOOL)retainFirst 
     secondDelegate:(id <NSObject>)secondDelegate retainSecond:(BOOL)retainSecond { 
    self.targetProtocol = targetProtocol; 
    self.delegates = @[[SOOptionallyRetainHolder holderWithTarget:firstDelegate retainTarget:retainFirst], 
      [SOOptionallyRetainHolder holderWithTarget:secondDelegate retainTarget:retainSecond]]; 
    return self; 
} 

+ (id)proxyForProtocol:(Protocol *)targetProtocol firstDelegate:(id <NSObject>)firstDelegate retainFirst:(BOOL)retainFirst 
     secondDelegate:(id <NSObject>)secondDelegate retainSecond:(BOOL)retainSecond { 
    return [[self alloc] initWithProtocol:targetProtocol 
          firstDelegate:firstDelegate 
           retainFirst:retainFirst 
          secondDelegate:secondDelegate 
          retainSecond:retainSecond]; 

} 


+ (id)proxyForProtocol:(Protocol *)targetProtocol firstDelegateR:(id <NSObject>)firstDelegate secondDelegateNR:(id <NSObject>)secondDelegate { 
    return [self proxyForProtocol:targetProtocol firstDelegate:firstDelegate retainFirst:YES 
        secondDelegate:secondDelegate retainSecond:NO]; 
} 

- (BOOL)conformsToProtocol:(Protocol *)aProtocol { 
    if (self.targetProtocol == aProtocol) 
     return YES; 
    else 
     return NO; 
} 

- (NSObject *)findTargetForSelector:(SEL)aSelector { 
    for (SOOptionallyRetainHolder *holder in self.delegates) { 
     NSObject *del = holder.target; 
     if ([del respondsToSelector:aSelector]) 
      return del; 
    } 
    return nil; 
} 

- (BOOL)respondsToSelector:(SEL)aSelector { 

    BOOL superRes = [super respondsToSelector:aSelector]; 
    if (superRes) 
     return superRes; 

    NSObject *delegate = [self findTargetForSelector:aSelector]; 

    return (delegate != nil); 
} 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 
    NSObject *delegate = [self findTargetForSelector:sel]; 
    if (delegate != nil) 
     return [delegate methodSignatureForSelector:sel]; 
    else 
     return nil; 
} 

- (void)forwardInvocation:(NSInvocation *)invocation { 
    NSObject *delegate = [self findTargetForSelector:invocation.selector]; 
    if (delegate != nil) 
     [invocation invokeWithTarget:delegate]; 
    else 
     [super forwardInvocation:invocation]; // which will effectively be [self doesNotRecognizeSelector:invocation.selector]; 
} 

@end 

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

Обратите внимание, что это можно сделать в соответствии с несколькими протоколами (UITableViewDelegate + UITableViewDataSource), но я не беспокоил.

Теперь с помощью этой магии мы можем просто присоединиться к двум классам, которые реализуют протокол UITableViewDataSource и получают желаемый объект. Но я думаю, что имеет смысл создать более явный протокол для второго делегата, чтобы показать, что некоторые методы не будут перенаправлены в любом случае.

@objc public protocol MyTableDataSource: NSObjectProtocol { 


    @objc optional func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 

    // copy here all the methods except the ones you've implemented 

} 

Теперь мы можем иметь наш LibTableDataSource, как

class LibTableDataSource: NSObject, UIKit.UITableViewDataSource { 

    class func wrap(_ dataSource: MyTableDataSource) -> UITableViewDataSource { 
     let this = LibTableDataSource() 
     return SOMulticastProxy.proxy(for: UITableViewDataSource.self, firstDelegateR: this, secondDelegateNR: dataSource) as! UITableViewDataSource 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return your logic here 
    } 

    func numberOfSections(in tableView: UITableView) -> Int { 
     return your logic here 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     return your logic here 
    } 
} 

Предполагая externalTableDataSource является объект класса пользователя библиотеки, которая реализует MyTableDataSource протокол, использование просто

let wrappedTableDataSource: UITableViewDataSource = LibTableDataSource.wrap(externalTableDataSource) 

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

SOOptionallyRetainHolder.h

@interface SOOptionallyRetainHolder : NSObject 
@property(nonatomic, readonly) id <NSObject> target; 

+ (instancetype)holderWithTarget:(id <NSObject>)target retainTarget:(BOOL)retainTarget; 
@end 

SOOptionallyRetainHolder.m

@interface SOOptionallyRetainHolder() 
@property(nonatomic, readwrite) NSValue *targetNonRetained; 
@property(nonatomic, readwrite) id <NSObject> targetRetained; 
@end 

@implementation SOOptionallyRetainHolder { 
@private 

} 

- (id)initWithTarget:(id <NSObject>)target retainTarget:(BOOL)retainTarget { 
    if (!(self = [super init])) return self; 
    if (retainTarget) 
     self.targetRetained = target; 
    else 
     self.targetNonRetained = [NSValue valueWithNonretainedObject:target]; 

    return self; 
} 

+ (instancetype)holderWithTarget:(id <NSObject>)target retainTarget:(BOOL)retainTarget { 
    return [[self alloc] initWithTarget:target retainTarget:retainTarget]; 
} 

- (id <NSObject>)target { 

    return self.targetNonRetained != nil ? self.targetNonRetained.nonretainedObjectValue : self.targetRetained; 
} 

@end 

Попытка # 2 (наследование от класса Obj-C)

Если иметь опасные SOMulticastProxy в вашем коде выглядит немного как излишним, вы можете создать более специализированный базовый класс SOTotallyInternalDelegatingBaseLibDataSource:

SOTotallyInternalDelegatingBaseLibDataSource.h

@interface SOTotallyInternalDelegatingBaseLibDataSource : NSObject <UITableViewDataSource> 
- (instancetype)initWithDelegate:(NSObject *)delegate; 

@end 

SOTotallyInternalDelegatingBaseLibDataSource.m

#import "SOTotallyInternalDelegatingBaseLibDataSource.h" 


@interface SOTotallyInternalDelegatingBaseLibDataSource() 
@property(nonatomic) NSObject *delegate; 

@end 

@implementation SOTotallyInternalDelegatingBaseLibDataSource { 

} 

- (instancetype)initWithDelegate:(NSObject *)delegate { 
    if (!(self = [super init])) return self; 

    self.delegate = delegate; 

    return self; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    [self doesNotRecognizeSelector:_cmd]; 
    return 0; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    [self doesNotRecognizeSelector:_cmd]; 
    return nil; 
} 


#pragma mark - 

- (BOOL)respondsToSelector:(SEL)aSelector { 

    BOOL superRes = [super respondsToSelector:aSelector]; 
    if (superRes) 
     return superRes; 


    return [self.delegate respondsToSelector:aSelector]; 
} 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 
    NSMethodSignature *superRes = [super methodSignatureForSelector:sel]; 
    if (superRes != nil) 
     return superRes; 

    return [self.delegate methodSignatureForSelector:sel]; 

} 

- (void)forwardInvocation:(NSInvocation *)invocation { 
    [invocation invokeWithTarget:self.delegate]; 
} 

@end 

А затем сделайте свой LibTableDataSource почти таким же, как в попытке # 1

class LibTableDataSource: SOTotallyInternalDelegatingBaseLibDataSource { 

    class func wrap(_ dataSource: MyTableDataSource) -> UITableViewDataSource { 
     return LibTableDataSource2(delegate: dataSource as! NSObject) 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return your logic here 
    } 

    func numberOfSections(in tableView: UITableView) -> Int { 
     return your logic here 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     return your logic here 
    } 
} 

, а использование абсолютно идентично используемому с попыткой № 1.Также это решение еще проще реализовать два протокола (UITableViewDelegate + UITableViewDataSource).

Немного больше мощности Obj-C волшебной

На самом деле вы можете использовать Obj-C магию, чтобы сделать MyTableDataSource протокол отличается от UITableDataSource в именах методов, а не копировать-вставить их и даже изменять такие параметры, как не передавая UITableView вообще или передавая свой пользовательский объект вместо UITableView. Я сделал это один раз, и это сработало, но я не рекомендую это делать, если у вас нет веских оснований для этого.

+0

Благодарим вас за ответ. Интересный материал об Obj-C, о котором я не знал, так как я являюсь Swift dev :) –

+1

массив делегатов сохранит объекты делегатов. Это нехорошее решение imho. Но я не буду голосовать. Это остается интересным подходом. –

+0

@ GiuseppeLanza, спасибо, что напомнили мне об этом. Я не написал код Obj-C в течение некоторого времени и просто забыл об этом. См. Мое обновление, в котором используется вспомогательный класс 'SOOptionallyRetainHolder', который решает эти проблемы. @GuyDahher, вы также можете взглянуть на обновленный код, который позволяет избежать утечки памяти. – SergGr

0

Я думаю, что лучший способ сделать это - подкласс UIViewController в вашем классеA и реализовать UITableViewDataSource. Чтобы избежать вызова необходимых методов, реализованных в ClassA, просто введите ключевое слово final в заполнение func.

Вот мое решение:

ClassA

import UIKit 

class ClassA: UIViewController, UITableViewDataSource { 

    // MARK: - Table view data source 

    final func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return 10 
    } 

    final func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) 

     cell.textLabel?.text = "Cell \(indexPath.row) in section \(indexPath.section)" 

     return cell 
    } 
} 

ClassB

import UIKit 

class ClassB: ClassA { 

    @IBOutlet weak var tableView: UITableView! 

    override func viewDidLoad() { 
     tableView.dataSource = self 
    } 

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 
     return "Header \(section)" 
    } 

    func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { 
     return "Footer \(section)" 
    } 
} 

Вот что вы получите:

enter image description here

+0

Что делать, если класс реализации не будет 'UIViewController'? Что, если это будет просто взгляд? Или что, если это будет другой тип контроллера, например. 'UITableViewController'? – Sulthan

+0

Согласен с Sulthan –

+0

Если вы хотите создать обычное поведение, вам необходимо создать пользовательскую библиотеку. Поэтому вам нужно подклассифицировать все объекты, которые вы хотите реализовать. На мой взгляд, нет другого способа сделать это. –

-1

Как уже упоминалось, нам нужен прокси-сервер. Чтобы создать прокси-сервер, который будет безопасным с точки зрения циклов сохранения, это довольно просто. Чтобы сделать его универсальным и гибким, это совершенно другая история. Здесь все знают, что шаблон делегата требует, чтобы объект делегата был слабым, чтобы избежать циклов сохранения (a сохраняет b и b сохраняет, так что никто не освобождается).

Непосредственным решением является N переменных в прокси-сервере, которые являются слабыми, конечно, так что вы можете направить на эти объекты делегат называет

class MyProxy: NSObject, UITableViewDelegate { 
    weak var delegate1: UITableViewDelegate? 
    weak var delegate2: UITableViewDelegate? 

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     delegate1?.tableView?(tableView, didSelectRowAt: indexPath) 
     delegate2?.tableView?(tableView, didSelectRowAt: indexPath) 
    } 
} 

Это, конечно, будет работать. Но это совсем не гибко. У вас может быть только 2 делегата, и, если вам нужно больше, вам нужно добавить делегат3 var, не забудьте обновить все ваши методы и так далее.

Кто-то может подумать «Хорошо, давайте иметь массив делегатов» ... Неправильно. Массив сохранит делегатов, которые больше не будут слабыми, и у нас будет цикл сохранения.

Раствор

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

public struct WeakContainer<T: NSObjectProtocol> { 
    public weak var delegate: T? 
} 

public struct WeakCollection<T: NSObjectProtocol> { 
    private var delegates: [WeakContainer<T>] = [WeakContainer<T>]() 

    public init(){} 

    public init(with delegate: T) { 
     add(object: delegate) 
    } 

    public mutating func add(object: T) { 
     let container = WeakContainer(delegate: object) 
     delegates.append(container) 
    } 

    public mutating func remove(object: T) { 
     guard let index = delegates.index(where: { 
      return object.isEqual($0.delegate) 
     }) else { return } 
     delegates.remove(at: index) 
    } 

    public mutating func execute(_ closure: ((_ object: T) throws -> Void)) rethrows { 
     let localDelegates = delegates 
     try localDelegates.forEach { (weakContainer) in 
      guard let delegate = weakContainer.delegate else { 
       cleanup() 
       return 
      } 
      try closure(delegate) 
     } 
    } 

    private mutating func cleanup() { 
     delegates.sort { (a, b) -> Bool in 
      return a.delegate == nil 
     } 
     while let first = delegates.first, first.delegate == nil { 
      delegates.removeFirst() 
     } 
    } 
} 

Это позволит вам сделать что-то вроде этого:

public class TableViewDelegateProxy: NSObject, UITableViewDelegate { 
    var delegates = WeakCollection<UITableViewDelegate>() 

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     delegates.execute { (delegate) in 
      delegate.tableView?(tableView, didSelectRowAt: indexPath) 
     } 
    } 
} 

Как вы можете видеть, эти несколько строк будут безопасными, так как weakCollection будет хранить слабую ссылку на делегат, это будет сама очистка, когда обнаружены освобожденные делегаты, и она может содержать объекты протокола, чтобы быть супер гибкими и сгибаться под ваши нужды.

+0

Хотя это правильно, ваш ответ на самом деле не отвечает на этот вопрос. Это комментарий для другого ответа и не должен быть здесь вообще. Ваш код будет принадлежать http://stackoverflow.com/questions/24127587/how-do-i-declare-an-array-of-weak-references-in-swift. Однако заметьте, что есть некоторые проблемы. Ваша 'cleanup' - очень сложный способ реализации' .filter'. Вероятно, ваш 'remove' должен использовать' ===', а не равенство. Ваша 'cleanup' может вызываться несколько раз в течение одного' execute'. – Sulthan

+0

Я не согласен. Хотя верно, что есть оптимизация, которую можно сделать с этим кодом, я считаю, что этот ответ отвечает на вопрос. Я объяснил, как создать прокси-сервер, который может содержать n делегатов безопасно, а не только два. Мне жаль, что вы не согласны, но я не думаю, что заслужил бы ниспровержение. –

+0

Я не спускал вниз, я рассматривал флаг :) Я только указывал, что вы не отвечаете на вопрос OP. Вы добавляете класс утилиты, который может использоваться как часть одного решения, но вы сами не даете фактическое решение. – Sulthan

 Смежные вопросы

  • Нет связанных вопросов^_^