2016-08-16 170 views
6

我有一個Objective-C協議,主要用於Objective-C對象和一個或兩個Swift對象。在ObjC協議上的協議擴展

我想在Swift中擴展協議並添加2個函數。一個註冊通知,另一個處理通知。

如果我添加這些

func registerForPresetLoadedNotification() { 
    NSNotificationCenter.defaultCenter().addObserver(self as AnyObject, 
                selector: #selector(presetLoaded(_:)), 
                name: kPresetLoadedNotificationName, 
                object: nil) 
} 

func presetLoaded(notification: NSNotification) { 

} 

我得到它說Argument of '#selector' refers to a method that is not exposed to Objective-C

如果我然後標記presetLoaded爲@objc我得到它說@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

我也是一個錯誤的#selector錯誤不能將協議擴展標記爲@objc

當我創建Objective-C協議作爲Swift協議我得到同樣的錯誤。

有沒有一種方法可以實現這一點,將使用協議的Objective-C和Swift類?

+0

如何擴展協議並將這些函數定義爲_optional_ functions在Objective-C中並在Swift中實現它們?只是一個想法。 – skim

+0

我的想法是在擴展中實現註冊函數,然後覆蓋objective-c和swift類中的通知處理函數。我需要在objective-c類中設置參數,在Swift中實現它們不會工作。 – jarryd

+0

我不確定我是否正確理解你。這聽起來像你想要做的是擴展(添加功能到現有的協議),並在ObjC中提供一個默認實現,並在Swift中重寫該實現的一部分。它是否正確? – skim

回答

3

事實上,你不能真正標記協議擴展的函數,如@objc(或動態,其是由方式當量)。 Objective-C運行時只允許調用類的方法。如果你真的想通過協議擴展,我可以提出以下解決方案(假設你的原始協議被命名爲ObjcProtocol)。

讓我們爲我們的通知處理器的包裝:

final class InternalNotificationHandler { 
    private let source: ObjcProtocol 

    init(source: ObjcProtocol) { 
     // We require source object in case we need access some properties etc. 
     self.source = source 
    } 

    @objc func presetLoaded(notification: NSNotification) { 
     // Your notification logic here 
    } 
} 

現在,我們需要擴大我們的ObjcProtocol引進所需的邏輯

import Foundation 
import ObjectiveC 

internal var NotificationAssociatedObjectHandle: UInt8 = 0 

extension ObjcProtocol { 
    // This stored variable represent a "singleton" concept 
    // But since protocol extension can only have stored properties we save it via Objective-C runtime 
    private var notificationHandler: InternalNotificationHandler { 
     // Try to an get associated instance of our handler 
     guard let associatedObj = objc_getAssociatedObject(self, &NotificationAssociatedObjectHandle) 
      as? InternalNotificationHandler else { 
      // If we do not have any associated create and store it 
      let newAssociatedObj = InternalNotificationHandler(source: self) 
      objc_setAssociatedObject(self, 
            &NotificationAssociatedObjectHandle, 
            newAssociatedObj, 
            objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) 
      return newAssociatedObj 
     } 

     return associatedObj 
    } 

    func registerForPresetLoadedNotification() { 
     NSNotificationCenter.defaultCenter().addObserver(self, 
                 selector: #selector(notificationHandler.presetLoaded(_:)), 
                 name: kPresetLoadedNotificationName, 
                 object: self) 
    } 

    func unregisterForPresetLoadedNotification() { 
     // Clear notification observer and associated objects 
     NSNotificationCenter.defaultCenter().removeObserver(self, 
                  name: kPresetLoadedNotificationName, 
                  object: self) 
     objc_removeAssociatedObjects(self) 
    } 
} 

我知道這可能看起來不那麼優雅,所以我我真的會考慮改變核心方法。

一個注意:你這樣做可能要限制你的協議擴展

extension ObjcProtocol where Self: SomeProtocolOrClass 
0

我發現了一個辦法做到這一點:)只要避免@objc一起:d

//Adjusts UITableView content height when keyboard show/hide 
public protocol KeyboardObservable { 
    func registerForKeyboardEvents() 
    func unregisterForKeyboardEvents() 
} 

extension KeyboardObservable where Self: UITableView { 

    public func registerForKeyboardEvents() { 
     NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) { notification in 
      self.keyboardDidShow(notification) 
     } 
     NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { notification in 
      self.keyboardWillHide(notification) 
     } 
    } 

    private func keyboardDidShow(_ notification: Notification) { 
     let rect = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue 
     let height = rect.height 
     var insets = UIEdgeInsetsMake(0, 0, height, 0) 
     insets.top = contentInset.top 
     contentInset = insets 
     scrollIndicatorInsets = insets 
    } 

    private func keyboardWillHide(_ notification: Notification) { 
     var insets = UIEdgeInsetsMake(0, 0, 0, 0) 
     insets.top = contentInset.top 
     UIView.animate(withDuration: 0.3) { 
      self.contentInset = insets 
      self.scrollIndicatorInsets = insets 
     } 
    } 

    public func unregisterForKeyboardEvents() { 
     NotificationCenter.default.removeObserver(self) 
    } 

} 

class CreateStudentTableView: UITableView, KeyboardObservable { 

    init(frame: CGRect, style: UITableViewStyle) { 
    super.init(frame: frame, style: style) 
    registerForKeyboardEvents() 
    } 

    required init?(coder aDecoder: NSCoder) { 
    fatalError("init(coder:) has not been implemented") 
    } 

    deinit { 
    unregisterForKeyboardEvents() 
    } 
}