首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ObjC协议的协议扩展

ObjC协议的协议扩展
EN

Stack Overflow用户
提问于 2016-08-16 17:19:15
回答 2查看 23.3K关注 0票数 23

我有一个objects协议,主要由objects对象和一两个Swift对象使用。

我想在Swift中扩展该协议,并添加2个函数。一个注册通知,另一个处理通知。

如果我加上这些

代码语言:javascript
复制
func registerForPresetLoadedNotification() {
    NSNotificationCenter.defaultCenter().addObserver(self as AnyObject,
                                                     selector: #selector(presetLoaded(_:)),
                                                     name: kPresetLoadedNotificationName,
                                                     object: nil)
}

func presetLoaded(notification: NSNotification) {
    
}

我在#选择器上有一个错误,上面写着:

“选择器”的参数是指不暴露于目标C的方法。

如果我将presetLoaded标记为@objc,则会得到一个错误,即:

@objc只能用于类、@objc协议和类的具体扩展的成员。

我也不能将协议扩展标记为@objc。

当我以Swift协议的形式创建Objective协议时,我也会遇到同样的错误。

是否有一种方法可以实现这个目标-C和Swift类使用该协议?

EN

回答 2

Stack Overflow用户

发布于 2016-08-16 21:08:19

实际上,不能将协议扩展的函数标记为@objc (或者dynamic,顺便说一下,这是等价的)。对象-C运行时只允许分派类的方法。

在您的特殊情况下,如果您真的想通过协议扩展来实现它,我可以提出以下解决方案(假设您的原始协议名为ObjcProtocol). )

让我们为我们的通知处理程序做一个包装:

代码语言:javascript
复制
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来引入所需的逻辑。

代码语言:javascript
复制
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)
    }
}

我知道这看起来可能不那么优雅,所以我真的会考虑改变一个核心方法。

一个注意事项:您可能需要限制协议扩展的

代码语言:javascript
复制
extension ObjcProtocol where Self: SomeProtocolOrClass
票数 9
EN

Stack Overflow用户

发布于 2018-01-29 11:36:10

我找到了一种方法:)只要避免@objc在一起:D

代码语言:javascript
复制
//Adjusts UITableView content height when keyboard show/hide
public protocol KeyboardObservable: NSObjectProtocol {
    func registerForKeyboardEvents()
    func unregisterForKeyboardEvents()
}

extension KeyboardObservable where Self: UITableView {

    public func registerForKeyboardEvents() {
        let defaultCenter = NotificationCenter.default

        var tokenShow: NSObjectProtocol!
        tokenShow = defaultCenter.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) { [weak self] (notification) in
            guard self != nil else {
                defaultCenter.removeObserver(tokenShow)
                return
            }
            self!.keyboardWilShow(notification as NSNotification)
        }

        var tokenHide: NSObjectProtocol!
        tokenHide = defaultCenter.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { [weak self] (notification) in
            guard self != nil else {
                defaultCenter.removeObserver(tokenHide)
                return
            }
            self!.keyboardWilHide(notification as NSNotification)
        }
    }

    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)
    }

}

示例

代码语言:javascript
复制
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")
  }
}
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38980887

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档