NSNotification子类与Swift 2.1中的Generics

时间:2015-12-01 21:54:53

标签: swift generics nsnotificationcenter

无法使用Generic payload对象创建NSNotification的子类。获取运行时错误或编译错误(请参阅下面的代码中的注释)。甚至可以使用Swift 2.1吗?任何想法都赞赏。谢谢!

运行时错误,因为NSNotification是抽象类(类集群) 编译错误因为应该使用指定的初始化程序。

public class Notification<T: Any>: NSNotification {

    private var _name: String
    private var _object: AnyObject?
    private var _payload: T?

    public override var name: String {
        return _name
    }

    public override var object: AnyObject? {
        return _object
    }

    public var payload: T? {
        return _payload
    }

    /// Always nil. Use payload
    public override var userInfo: [NSObject : AnyObject]? {
        return nil
    }

    /// Change to false to "swap" implementation
    #if true

    init(name: String, object: AnyObject? = nil, payload: T? = nil) {
        _name = name
        _object = object
        _payload = payload
        /*
          Runtime error:
          Terminating app due to uncaught exception
          'NSInvalidArgumentException', reason:
          '*** initialization method -initWithName:object:userInfo:
          cannot be sent to an abstract object of class
          _TtGC14__lldb_expr_1612NotificationSS_:
          Create a concrete instance!'
        */
        super.init(name: name, object: object, userInfo: nil)
    }

    #else

    convenience init(name: String, object: AnyObject? = nil, payload: T? = nil) {
        self.init()
        _name = name
        _object = object
        _payload = payload
    }

    init() {
        /// compiler error:
        /// must call a designated initializer of the superclass
        /// But using designated initializer cause runtime error listed above.
        super.init() 
    }

    #endif
}


let n = Notification<String>(name: "xyz", payload: "Hello")

2 个答案:

答案 0 :(得分:8)

From the docs,强调我的:

  

除了通知名称,对象和字典之外,您还可以将NSNotification子类化为包含信息。这些额外的数据必须在通知者和观察者之间达成一致。

     

NSNotification是一个没有实例变量的类集群。因此,您必须继承NSNotification并覆盖原始方法nameobjectuserInfo。您可以选择任何您喜欢的指定初始值设定项,但请确保初始值设定项不会调用[super init] NSNotification并非意图直接实例化,其init方法会引发异常。

现在没有办法从Swift代码中继承NSNotification,因为Swift没有&#34;未初始化的类&#34;并要求所有子类调用他们的超类init(在这种情况下,这是错误的做法)。

您必须在Objective-C中编写子类并将其桥接到您的Swift代码中。

不幸的是,即使您可以声明您的Objective-C类是通用的,该信息也会在桥接过程中丢失。 From the docs

  

除了这些Foundation集合类之外,Swift还忽略了Objective-C轻量级泛型。使用轻量级泛型的任何其他类型都会导入到Swift中,就好像它们是未参数化的一样。

:(

答案 1 :(得分:0)

我在没有子类化的情况下解决了原始问题。

档案 GenericNotification.swift

private let genericNotificationPayloadKey = "com.mc.notification-payload"

class GenericNotification<T: Any> {

   let payload: T
   let name: NSNotification.Name
   let object: Any?

   init(name: NSNotification.Name, object: Any? = nil, payload: T) {
      self.name = name
      self.object = object
      self.payload = payload
   }

   init?(notification: Notification) {
      guard let payload = notification.userInfo?[genericNotificationPayloadKey] as? T else {
         return nil
      }
      self.payload = payload
      name = notification.name
      object = notification.object
   }
}

extension GenericNotification {

   var notification: Notification {
      return Notification(name: name, object: object, userInfo: [genericNotificationPayloadKey: payload])
   }

   static func observe(name: NSNotification.Name,
                       object: Any? = nil,
                       queue: OperationQueue = .main,
                       handler: @escaping (T) -> Void) -> NotificationObserver {
      return NotificationObserver(name: name, object: object, queue: queue) {
         if let notification = GenericNotification(notification: $0) {
            handler(notification.payload)
         }
      }
   }

   func post(center: NotificationCenter = NotificationCenter.default) {
      center.post(notification)
   }
}

文件 NotificationObserver.swift

class NotificationObserver: NSObject {

   typealias Handler = ((Foundation.Notification) -> Void)

   private var notificationObserver: NSObjectProtocol!
   var notificationHandler: Handler?
   private let notificationName: NSNotification.Name
   private let notificationObject: Any?

   init(name: NSNotification.Name, object: Any? = nil, queue: OperationQueue = .main, handler: Handler? = nil) {
      notificationName = name
      notificationObject = object
      notificationHandler = handler
      super.init()
      notificationObserver = NotificationCenter.default.addObserver(forName: name, object: object, queue: queue) { [weak self] in
         self?.handleNotification($0)
      }
   }

   deinit {
      NotificationCenter.default.removeObserver(notificationObserver, name: notificationName, object: notificationObject)
   }

   /// Calls block which was passed as *usingBlock* parameter.
   /// Child classes may override to change default behaviour.
   /// - parameter notification: Notification to handle.
   func handleNotification(_ notification: Foundation.Notification) {
      notificationHandler?(notification)
   }
}

<强>用法

// Send notification
let action = MyType.doSomething
GenericNotification(name: .myNotificationName, payload: action).post()

// Receive notification
private var notificationObservers: [NotificationObserver] = []
...
notificationObservers.append(GenericNotification<MyType>.observe(name: .myNotificationName) { instanceOfMyType in
    // Got `instanceOfMyType` which is `MyType.doSomething`
})