6

I am not sure this can be done, or if it's even unrecommended.

What I am trying to achieve is the following:

I have a 2 classes classA and classB that have a reference to the same UITableview instance. What I want is for classA to take care of the implementation of the 2 required methods of the UITableViewDataSource protocol:

  • numberOfRowsInSection
  • cellForRowAt

Then I want classB to be able to implement the other optional methods like titleForHeaderInSection for example.

So how can classA have default implementation of some protocol methods, and let classB be the a class that can build on top of what classB has done?

In a way, the problem that I am facing is the following: How can multiple classes be the datasource of a single UITableView?

EDIT: classA will be in a library that I am writing that takes care of building the core parts of the tableView. classB will be used by the 3rd party dev to mainly customise its appearance.

Guy Daher
  • 5,526
  • 5
  • 42
  • 67
  • 1
    Easily, either `classA` will redirect some calls to `classB` or you will create `classC` that will be the delegate and will redirect either to `A` and `B`. – Sulthan Feb 10 '17 at 16:42
  • @Sulthan I see. I want to make All of `UITableViewDataSource` methods available to `classB`, and it would be nice to avoid writing all the methods one by one in order to redirect. So From what I understand, option 1 wouldn't work for me. And same for option 2 since I will have to write a lot of code. Is there something that I am missing? (maybe there's an easy way to redirect that I am missing). Also edited the question with one more info. – Guy Daher Feb 10 '17 at 16:57
  • If ClassA exists solely for the purpose of implementing defaults you can take care of this in swift with a protocol extension instead. https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521 – Josh Homann Feb 10 '17 at 17:05
  • 1
    Could `classB` be a subclass of `classA`? That way, the tableView delegate would be `classB` which would only implement the optional methods. – Stephen Feb 10 '17 at 17:17
  • @Stephen That's possible, if subclassing makes sense for your model. I would recommend to rethink the architecture instead. – Sulthan Feb 10 '17 at 17:33
  • @Stephen I want to avoid that. classB will inherit from UIViewController or others (the choice of the 3rd party developer) – Guy Daher Feb 10 '17 at 20:09
  • @Sulthan Any help on what I mentioned in my first comment? Would really appreciate! Thanks for all the help – Guy Daher Feb 11 '17 at 11:49

5 Answers5

5

I think that the only solution without manually redirecting everything is to use the default implementation of protocol methods, e.g. :

protocol MyTableViewProtocol : UITableViewDelegate, UITableViewDataSource {

}

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

And then make ClassB to implement MyTableViewProtocol instead of UITableViewDelegate and UITableViewDataSource.

However, such a solution will not work because protocol extensions are not accessible by Obj-C.

I think a cleaner (and working) solution would be to create the implementation of numberOfRowsInSection and cellForRowAt outside the protocol and just let ClassB to call them inside the delegate method, e.g.:

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

Such a solution will be clearer for the user because it will contain less "magic".

Of course, the classic solution is to define your own delegate:

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

and redirect everything to it from your delegate.

This solution makes it impossible for ClassB to overwrite delegate function you don't want it to overwrite.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Great answer, especially the `clearer for the user` part. I think that it's going to be the one that I will use. Will see if there are more suggestions. However, I'm curious if you would suggest to use the above solution, or manually redirecting one? (cause I went with manually redirecting now, and it works fine. But downside is that there is a lot of boilerplate code, and some magic is happening behind the back of the user) – Guy Daher Mar 16 '17 at 12:41
  • 1
    @GuyDaher I don't think redirecting is an ideal solution but I have used it several times especially in situations when I also have a subclassed table view therefore I can create a better delegate that already uses my subclassed type. However, that was always for internal use in a project, not for an open library. – Sulthan Mar 16 '17 at 12:56
3

My answer consists of two parts. In first part I'd like to discuss your design decision and in second provide one more alternative solution using Obj-C magic.

Design considerations

It looks like you want ClassB to not be able to override your default implementation.

First of all, in such case you probably should also implement

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

in your ClassA for consistency or ClassB will be able to return something else there without ability to return additional cells.

Actually this prohibitive behavior is what I don't like in such design. What if the user of your library wants to add more sections and cells to the same UITableView? In this aspect design as described by Sulthan with ClassA providing default implementation and ClassB wrapping it to delegate and probably sometimes change the defaults seems preferable to me. I mean something like

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

Also such design has another advantage of not needing advanced Obj-C tricks to work in more complicated scenarios such as UITableViewDelegate because you don't have to implement optional methods you don't want in either of ClassA or ClassB and still can add methods you (library's user) need into ClassB.

Obj-C magic

Suppose that you still do want to make your default behavior to stand as the only possible choice for methods you've implemented but let customize other methods. Assume also that we are dealing with something like UITableView which is designed in heavily Obj-C way i.e. heavily relies on optional methods in delegates and doesn't provide any simple way to call Apple's standard behavior (this is not true for UITableViewDataSource but true for UITableViewDelegate because who knows how to implement something like

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

in backward and forward compatible way to match default Apple's style on every iOS).

So what's the solution? Using a bit of Obj-C magic we can create our class, that will have our default implementations for protocol methods we want such that if we provide to it another delegate that has some another optional methods implemented, our object will look like it has them too.

Attempt #1 (NSProxy)

First we start with a generic SOMulticastProxy which is kind of proxy that delegates calls to two objects (see sources of helper SOOptionallyRetainHolder further).

SOMulticastProxy.h

@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 is basically following: find first delegate that responds to required selector and forward call there. If neither of the delegates knows the selector - say that we don't know it. This is a more powerful than just automation of delegating all methods because SOMulticastProxy effectively merge optional methods from both passed objects without a need to provide somewhere default implementations for each of them (optional methods).

Note that it is possible to make it conform to several protocols (UITableViewDelegate + UITableViewDataSource) but I didn't bother.

Now with this magic we can just join two classes that both implement UITableViewDataSource protocol and get an object you want. But I think that it makes sense to create more explicit protocol for second delegate to show that some methods will not be forwarded anyway.

@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

}

Now we can have our LibTableDataSource as

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

Assuming externalTableDataSource is an object of the library user's class that implements MyTableDataSource protocol, usage is simply

let wrappedTableDataSource: UITableViewDataSource = LibTableDataSource.wrap(externalTableDataSource)

Here is the source for SOOptionallyRetainHolder helper class. SOOptionallyRetainHolder is a class that allows you to control wether object will be retained or not. This is useful because NSArray by default retains its objects and in typical usage scenario you want to retain first delegate and not retain the second one (thanks Giuseppe Lanza for mentioning this aspect that I totally forgot about initially)

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

Attempt #2 (inheritance from Obj-C class)

If having dangerous SOMulticastProxy in your codebase looks a bit like an overkill, you can create more specialized base class 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

And then make your LibTableDataSource almost the same as in Attempt #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
    }
}

and the usage is absolutely identical to the one with Attempt #1. Also this solution is even easier to make implement two protocols (UITableViewDelegate + UITableViewDataSource) at the same time.

A bit more on power of Obj-C magic

Actually you can use Obj-C magic to make MyTableDataSource protocol different from UITableDataSource in method names rather than copy-paste them and even change parameters such as not passing UITableView at all or passing your custom object instead of UITableView. I've done it once and it worked but I don't recommend doing it unless you have a very good reason to do it.

SergGr
  • 23,570
  • 2
  • 30
  • 51
  • Thank you for the answer. Interesting stuff about Obj-C that I didn't know about since I am a Swift dev :) – Guy Daher Mar 21 '17 at 08:21
  • 1
    delegates array will retain the delegates objects. It is not a good solution imho. But I will not down vote it. It remains an interesting approach. – Giuseppe Lanza Mar 21 '17 at 10:55
  • @GiuseppeLanza, thank you for reminding me about it. I haven't written Obj-C code for some time and just forgot about this. See my update that uses `SOOptionallyRetainHolder` helper class that addresses this issues. @GuyDahher, you might also take a look at the updated code that avoids memory leak. – SergGr Mar 21 '17 at 20:30
  • If you are in Obj-C, don't try to reimplement the wheel and use `[NSPointerArray weakObjectsPointerArray]`. – Sulthan Mar 21 '17 at 22:10
  • @Sulthan, I think you underestimate the problem. As you may see from the usage example, we want to retain `firstDelegate` (or `LibTableDataSource` will be deallocated) but don't want to retain the `secondDelegate` (or there will be a memory leak because we just retained back the `UIViewController`). I see no way to achieve this without "reinventing the wheel". Let me know if you see how to improve this code, but still meet that requirement? – SergGr Mar 21 '17 at 22:19
  • @SergGr I think you are making the problem unnecessarily complicated by your architecture. Instead of having `LibTableDataSource.wrap` creating an instance of `LibTableDataSource`, the owner of the data source should create `LibTableDataSource`, then the other delegate and then wrap them together using a proxy. Then the owner will own both delegates and the proxy and there will be no need for the proxy to own one of the delegates. – Sulthan Mar 22 '17 at 08:35
  • @Sulthan, I believe that simpler (external) API worth a bit of complication for (internal) implementation because I do implementation only once and API will be used many times. If there is no sense in enforcing the user of the library to hold an additional `LibTableDataSource` variable except for making my (library's developer) life a bit easier (and there seems none), I think it is a _bad_ tradeoff. It just creates unnecessary additional cognitive load for the user and also leaks my implementation details so I can't change them latter between my approaches #1 and #2. Not good at all. – SergGr Mar 22 '17 at 15:41
  • @SergGr Note the users will always have to retain something. They will have to retain at least the proxy returned from `LibTableDataSource.wrap`. If you design your classes correctly, there will be no tradeoffs and the users will still have to retain only one object. – Sulthan Mar 22 '17 at 16:59
  • @Sulthan, I'm not sure I get you proposal. Do you suggest that the user should retain `LibTableDataSource` and `LibTableDataSource` retains the proxy? In this case there is again a huge leak of implementation details as user should do something like `tableView.dataSource = libTableDataSource.buildRealDataSource()`. And now you also have to deal with possibility that someone would call that `buildRealDataSource` twice and you don't know why: is it because `UITableView` is rebuilt or is it because your `libTableDataSource` is shared. If you have a better design, please put it more explicitly. – SergGr Mar 22 '17 at 17:10
1

I think the best way to do this is to subclass UIViewController in your ClassA and implement UITableViewDataSource. To prevent calling of required methods implemented in ClassA, just put final keyword in the func implemetation.

Here is my solution:

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)"
    }
}

Here is what you get:

enter image description here

Shkelzen
  • 178
  • 1
  • 11
  • What if the implementing class won't be a `UIViewController`? What if it will be just a view? Or what if it will be another type of controller, e.g. `UITableViewController`? – Sulthan Mar 21 '17 at 21:55
  • Agreed with Sulthan – Guy Daher Mar 22 '17 at 07:45
  • If you want a custom behaviour, you need to create a custom library. So you need to subclass all objects you want to implement this behaviour. In my opinion, there is no other way to do it. – Shkelzen Aug 07 '17 at 07:43
0

You can do you things like this. People who write class B uses extension A to added UITableViewDataSource functions.

// 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]
    }
}
Owen Zhao
  • 3,205
  • 1
  • 26
  • 43
  • 1
    That's good answer thanks! my preference is @Sulthan s answer as it fits more the architecture that I want. – Guy Daher Mar 16 '17 at 12:43
  • The reusability of this solution is very limited though. What if you need multiple implementations? – Sulthan Mar 21 '17 at 21:53
  • @Sulthan I didn't see there were multiple implementations requirement in the question. So it would be a plus, but not requirement. Besides, my answer can limit class B from implementing functions like `func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)`. But your answer of protocol extension can't. Your answer relies on people who implement on class B to follow the rules, which is not a very good idea than mine. – Owen Zhao Mar 21 '17 at 22:43
  • @OwenZhao If you are writing a library, the possibility to have multiple tables with different implementations is definitely to be expected. – Sulthan Mar 21 '17 at 23:02
  • @Sulthan I didn't see there was a need for open library. it is OK for a internal libray. – Owen Zhao Mar 21 '17 at 23:11
-1

As someone else mentioned we need a proxy. To design a proxy that will be safe in terms of retain cycles it's fairly easy. To make it generic and flexible it is a totally different story. Everybody here knows that the delegate pattern requires the delegate object to be weak to avoid retain cycles (a retains b and b retains a so nobody is deallocated).

The immediate solution is to have N variables in your proxy that are weak of course so that you can forward to those objects the delegate calls

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

This of course will work. But it is not flexible at all. You can have just 2 delegates, and clearly if you want more you have to add delegate3 var, remember to update all your methods and so on.

Someone might think "Fine, let's have a delegates array"... Wrong. The array would retain the delegates that will no longer be weak and we will have a retain cycle.

The solution

To get things flexible I've created a weak collection. This code will allow you to have a collection of weak elements by using generics. You will be able to implement as many proxy as you want and these proxy can hold as many delegates you prefer.

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

This will allow you to do something like this:

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

As you can see, these few lines will be safe, as the weakCollection will store a weak reference to the delegates, it will cleanup herself when released delegates are found and it can contain objects of a protocol to be super flexible and bend to your needs.

Giuseppe Lanza
  • 3,519
  • 1
  • 18
  • 40
  • While this is correct, your answer is not actually answering *this* question. It is a comment for another answer and shouldn't be here at all. Your code would belong to http://stackoverflow.com/questions/24127587/how-do-i-declare-an-array-of-weak-references-in-swift. However note there are some problems. Your `cleanup` is a very complicated way to implement `.filter`. Your `remove` should probably use `===` and not equality. Your `cleanup` can be called multiple times during one `execute`. – Sulthan Mar 21 '17 at 22:04
  • I disagree. While it is true that there are optimisation that can be made to that code, I believe that this answer is answering the question. I explained how to build a proxy that can hold n delegates safely, not just two. I'm sorry that you disagree but I don't think I should deserve a downvote. – Giuseppe Lanza Mar 22 '17 at 07:57
  • I didn't downvote I was considering a flag :) I was only pointing out that you are not answering the OP's question. You are adding a utility class that could be used as part of one solution but you are not giving the actual solution yourself. – Sulthan Mar 22 '17 at 08:30