在一个Swift委托数组中查找委托

6

在我的removeDelegate方法中,我希望能够检查是否已经有了一个委托,然后再进行删除。要如何实现呢?

以下是我目前的代码:

protocol LocationManagerDelegate {
    func locationManagerDidUpdateLocation(
        oldLocation: CLLocationCoordinate2D,
        currentLocation: CLLocationCoordinate2D
    )
}

class LocationManager: NSObject {
    private var _delegates = [LocationManagerDelegate]()

    func removeDelegate(delegate:LocationManagerDelegate) {
        if contains(_delegates, delegate) {
            // Remove delegate
        }
    }
}

然而,这个 'if 包含' 行会给我以下错误:
无法使用 '(@lvalue Array< LocationManagerDelegate >!,LocationManagerDelegate)' 类型的参数列表调用 'contains'。
3个回答

16

针对Swift 4.2的更新:

假设委托实际上是一个类的实例,您可以通过从“class”继承来要求在协议中实现:

protocol LocationManagerDelegate: class {
    // ...
}

接着使用 firstIndex(where:) 方法,并使用 "恒等运算符" ===

class LocationManager: NSObject {
    private var _delegates = [LocationManagerDelegate]()
    
    func removeDelegate(delegate:LocationManagerDelegate) {
        if let index = _delegates.firstIndex(where: { $0 === delegate }) {
            _delegates.remove(at: index)
        }
    }
}

旧版答案(Swift 1):

有两个略有不同的contains()函数:

func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool

func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: (S.Generator.Element) -> L) -> Bool

您正在使用第一个选项,它要求序列元素符合Equatable协议,即它们可以通过==进行比较。

假设委托实际上是一个类的实例,您可以通过“继承”自“class”来在协议中要求:

protocol LocationManagerDelegate : class {
    // ...
}

然后使用第二个基于谓词的版本的contains(),并使用身份运算符===

func removeDelegate(delegate:LocationManagerDelegate) {
    if contains(_delegates, { $0 === delegate }) {
        // Remove delegate
    }
}

要从数组中删除对象,您需要获取其索引,因此可以使用https://dev59.com/W4Lba4cB1Zd3GeqPkuNm#25543084中的findIdenticalObject()函数:

func findIdenticalObject<T : AnyObject>(array: [T], value: T) -> Int? {
    for (index, elem) in enumerate(array) {
        if elem === value {
            return index
        }
    }
    return nil
}

然后从数组中查找并删除

func removeDelegate(delegate:LocationManagerDelegate) {
    if let index = findIdenticalObject(_delegates, delegate) {
        _delegates.removeAtIndex(index)
    }
}

1
需要谓词语法来实现这个似乎很奇怪。它为什么不能默认回退到恒等运算符呢?(就像 [NSObject -isEqual:] 那样)? - Aaron Brager
@AaronBrager:我尝试了一下,首先通过要求“ LocationManagerDelegate : NSObjectProtocol”,但它没有起作用,NSObjectProtocol不符合Equatable。但可能有更好的解决方案。 - Martin R
在 Swift 5.1 中,index(where:) 已被弃用,请改用 firstIndex(where:)。 - Vijayendra
1
@Vijayendra: 感谢您的通知,我已相应更新了代码。 - Martin R

2
contains的参数必须实现Equatable协议,因为它被定义为:
public func contains<T:Equatable>(left:[T], right:T) -> Bool

由于无法指示LocationManagerDelegate实现了Equatable协议,因此我认为您无法使用它。显而易见的尝试是:

protocol LocationManagerDelegate : Equatable {
    ...
}

但是,当你尝试声明数组时,这种方法会失败,因为Equatable使用Self。

我能想到的最佳选择是:

func removeDelegate(delegate:LocationManagerDelegate) {
    _delegates = filter(_delegates) { return $0 !== delegate }
}

呵呵,我刚刚花了大约十分钟想出同样的“filter()”。这会教训我在分心之前先阅读所有答案... - Matt Gibson

0
protocol LocationManagerDelegate {
    // ...
    var index_delegate:Int?{get set}
}



class LocationManager {
    private var delegates:[LocationManagerDelegate] = []

    func add(delegate: LocationManagerDelegate?){
        if let d = delegate {
            self.delegates.append(d)
            let index = self.delegates.count - 1
            self.delegates[index].index_delegate = index
            }
    }

    func remove(delegate: LocationManagerDelegate) {
        delegates = delegates.filter({ return $0.index_delegate != delegate.index_delegate })
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接