【问题标题】:How to remove specific object from array? [duplicate]如何从数组中删除特定对象? [复制]
【发布时间】:2017-04-08 14:55:04
【问题描述】:

如果调用者传入一个 MyDelegate 类型的对象并且它在数组中,我想将它从数组中移除。

protocol MyDelegate {
}

private var delegates = [MyDelegate]()
...
...
func removeDelegate(_ delegate: MyDelegate) {
    if let index = self.delegates.index(where: { $0 == delegate }) {
        log.trace("Removing delegate \(delegate)");
        self.delegates.remove(at: index)
    }
}
  1. 有没有更简单的方法来做到这一点?

  2. 此条件“{ $0 == delegate }”导致错误“无法将类型 '(OptionalNilComparisonType) -> Bool' 的值转换为预期的参数类型 '( ) -> 布尔'"。我怎样才能解决这个问题?我试过添加?和 !但仍然没有完全理解 Swift 的可选概念。

我正在使用 Xcode 8.2.1 和 Swift 3(?)。

【问题讨论】:

  • 代理应该是类的实例吗? 然后你可以定义一个“类协议”protocol MyDelegate: class { }并使用身份运算符进行比较:index(where: { $0 === delegate })

标签: arrays swift xcode8 optional


【解决方案1】:
  1. 有没有更简单的方法来做到这一点?

可以在访问delegates 成员时忽略self,并将index(where:) 调用的结果索引烘焙到对Optionalmap 方法的调用中:

func removeDelegate(_ delegate: MyDelegate) {
    delegates.index(where: { $0 == delegate})
        .map { delegates.remove(at: $0) }
}

如果没有找到这样的delegate 对象,则上面的表达式只会返回nil(未捕获的结果)。


这个条件“{ $0 == delegate }”给出导致错误, “无法将 '(OptionalNilComparisonType) -> Bool' 类型的值转换为 预期的参数类型'() -> Bool'”。我该如何解决这个问题?我试过了 添加?!,但仍不能完全理解Swift 的可选 概念。

这是 Swift 中一种模糊错误消息的又一个例子。核心错误是MyDelegate没有定义==操作符(不符合Equatable)。

但是,在您进行编辑后,您显示MyDelegate 是一个协议,因此如果您让它符合Equatable,您将无法(因为它将包含Self 类型要求)使用@ 987654345@ 作为具体类型(仅作为泛型上的类型约束)。

如果您的具体委托对象是引用对象 (class),并且您想在测试的意义上测试对象相等性是否都引用同一个对象(对象引用),您可以使用 @987654321 @ 可用于类实例。将MyDelegate(在您的编辑后将其显示为协议)限制为仅类,

protocol MyDelegate: class { /* ... */ }

并在上面的index(where:) 调用中测试ObjectIdentifier 的相等性:

func removeDelegate(_ delegate: MyDelegate) {
    delegates.index(where: { ObjectIdentifier($0) == ObjectIdentifier(delegate) })
        .map { delegates.remove(at: $0) }
}

查看ObjectIdentifier 的源代码,我们看到这将比较两个delegate 实例的底层原始ptr 值;来自swift/stdlib/public/core/ObjectIdentifier.swift

public static func == (x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
  return Bool(Builtin.cmp_eq_RawPointer(x._value, y._value))
}

正如@MartinR 在上述问题的 cmets 中所提到的,您也可以直接将 === 标识运算符用于类实例,而不是通过 ObjectIdentifier

func removeDelegate(_ delegate: MyDelegate) {
    delegates.index(where: { $0 === delegate })
        .map { delegates.remove(at: $0) }
}

为了完整起见,我们可以通过查看swift/stdlib/public/core/Equatable.swift 来验证=== 使用与== 运算符相同的Builtin 方法ObjectIdentifier

public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
  switch (lhs, rhs) {
  case let (l?, r?):
    return Bool(Builtin.cmp_eq_RawPointer(
        Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(l)),
        Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(r))
      ))
  case (nil, nil):
    return true
  default:
    return false
  }
}

【讨论】:

  • 没有像 java 和 c# 中的默认对象相等的 Swift 概念吗?
  • @RichAmberale 一般来说,没有。 Object​Identifier 可用于类实例,因此如果您将 MyDelegate (在编辑后将其显示为协议)限制为仅类,您可以测试 ObjectIdentifier:s 的 $0 的相等性和上面的delegate。这实质上是测试两者是否引用(对象引用)到同一个对象,而不是在某些预定义的== 运算符的意义上它们是否相等。
  • @MartinR 感谢您的链接!我在ObjectIdentifier 的源代码中看到了相同的评论部分,但正在四处寻找=== 的实际实现(以验证它很可能调用相同的Builtin 方法)。
  • @RichAmberale 乐于提供帮助。这可能是个人喜好,但我自己在使用 Swift 时通常会避免使用 Obj-C 功能,所以我自己可能更喜欢 === / ObjectIdentifier 而不是 NSObjectProtocol:s isEqual(...) 方法。跨度>
  • 感谢您的链接,我已经更新了其他答案。 - 我不知道。从github.com/apple/swift/blob/master/include/swift/AST/… 看来,似乎没有必要:“CastToUnknownObject 具有类型 (T) -> Builtin.UnknownObject。”和“BridgeToRawPointer 具有类型 (T) -> Builtin.RawPointer”——但我对“内置 SIL 操作”没有更深入的了解 :)
猜你喜欢
  • 1970-01-01
  • 2013-04-24
  • 2019-12-03
  • 1970-01-01
  • 2016-09-10
  • 2012-06-17
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
相关资源
最近更新 更多