首页 > 解决方案 > 只有在可以调用另一个方法的情况下,如何使一个方法出现在“重载集”中?

问题描述

考虑 NotificationCenter 上的一个扩展,它对符合某些协议的通知对象执行特殊操作(是的,这些可能更常见,但它是帮助演示我的真实问题的示例)。

extension NotificationCenter {

  func addObserver<Note: BasicNotification>(using block: @escaping (Note) -> ())
  -> NotificationToken {
    let observer = addObserver(forName: Note.notificationName, object: nil,
        queue: nil, using: { note in
      block(Note(notification: note))
    })
    return NotificationToken(observer: observer, center: self)
  }

  func addObserver<Note: CustomNotification>(using block: @escaping (Note) -> ())
  -> NotificationToken {
    let observer = addObserver(forName: Note.notificationName, object: nil,
        queue: nil, using: { note in
      block(note.object as! Note)
    })
    return NotificationToken(observer: observer, center: self)
  }

}

现在,考虑一下我们何时希望此通知仅触发一次,然后注销...

extension NotificationCenter {

  func observeOnce<Note: BasicNotification>(using block: @escaping (Note) -> ()) {
    var token: NotificationToken!
    token = addObserver(using: { (note: Note) in
      block(note)
      token.reset()
    })
  }

  func observeOnce<Note: CustomNotification>(using block: @escaping (Note) -> ()) {
    var token: NotificationToken!
    token = addObserver(using: { (note: Note) in
      block(note)
      token.reset()
    })
  }

}

这是完全相同的代码。我真正想要的是一种observeOnce方法——我不想写两个。

如果我不使用任何条件一致性......

  func observeOnce<Note>(using block: @escaping (Note) -> ()) {
    var token: NotificationToken!
    token = addObserver(using: { (note: Note) in
      block(note)
      token.reset()
    })
  }

我得到一个Cannot invoke 'addObserver' with an argument list of type '(using: (Note) -> ())'非常有意义的错误。

如果我使用通用的基本协议(两者都符合)......

  func observeOnce<Note: SomeNotification>(using block: @escaping (Note) -> ()) {
    var token: NotificationToken!
    token = addObserver(using: { (note: Note) in
      block(note)
      token.reset()
    })
  }

我得到了完全相同的错误,这不太有意义 - 我希望这个调用是模棱两可的,而不是根本不存在。

看到&操作员的意思是遵守多个协议后,我确​​实尝试BasicNotification | CommonNotification在极不可能的情况下使用它可能有一些意义......但当然它没有用。

我也尝试了很多其他的替代方案,但无济于事。observeOnce如果其他任何一个都可以打电话,我想做的是可以打电话。

在 C++ 中,我会做这样的事情(没有通过编译器运行它 - 希望你能得到我想要做的事情)......

template <typename T>
auto observeOnce(std::function<void (T)> block)
-> decltype(void(this->addObserver(std::move(block))))
{
  // do my stuff here
}

上面的代码基本上意味着该函数observeOnce只有在addObserver(std::move(block))可以调用的情况下才会出现在重载集中。

那么,完成同样事情的快速方法是什么?

标签: swift

解决方案


您可以使用的一个技巧是重新组织您的代码。与其在 中创建多个(通用)addObserver方法NotificationCenter,不如将它们移动到您的通知类型(基本和自定义通知)中,并使用协议将它们形式化。然后,您可以使用单个 func 扩展此协议以添加addOnce逻辑。当您的基本和自定义通知实现此协议时,它们会自动继承此新addOnce功能,而无需任何重复代码。

重构方案

例子

下面是一个关于如何实现这个想法的例子:

首先创建一个ObservableNotification允许将观察者添加blockNotificationCenter.

protocol ObservableNotification {
    static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken
}

然后,让你的 Notification 协议继承自这个ObservableNotification协议

protocol NameableNotification {
    static var notificationName: NSNotification.Name {get}
}

protocol CustomNotification: NameableNotification, ObservableNotification {}
protocol BasicNotification: NameableNotification, ObservableNotification {
    init(notification: Notification)
}

并使用协议扩展将您的addObserver方法(从NotificationCenter)移动到相应的协议作为默认实现:

extension BasicNotification {
    static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
        let observer = center.addObserver(forName: Self.notificationName, object: nil, queue: nil) { note in
            block(Self(notification: note))
        }
        return NotificationToken(observer: observer, center: center)
    }
}

extension CustomNotification {
    static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
        let observer = center.addObserver(forName: Self.notificationName, object: nil, queue: nil) { note in
            block(note.object as! Self)
        }
        return NotificationToken(observer: observer, center: center)
    }
}

这样,您可以ObservableNotification使用该方法的默认实现来扩展协议,并且observeOnce您将能够在符合ObservableNotification(在您的情况下)的每种类型上调用它。像这样:CustomNotificationBasicNotification

extension ObservableNotification {
    static func addOneTimeObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
        var token: NotificationToken!
        token = addObserver(to: center) {
            block($0)
            token.reset()
        }
        return token
    }
}

推荐阅读