如何同时使用带有可选和扩展的@objc 协议? [英] How to use @objc protocol with optional and extensions at the same time?

查看:56
本文介绍了如何同时使用带有可选和扩展的@objc 协议?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这段代码无法编译,听起来可能很愚蠢,但我会解释为什么它如此重要!

This code does not compile and might sound stupid as it is, but i'll explain why it's so important!

@objc protocol p {
    optional func f1()
    func f2()
}

extension p {
    func f1() { }
    func f2() { }
}


class foo: p {
}

编译器说 Type c 不符合协议 'p' 这可能是因为您不能同时使用 @objc 可选和扩展(并且在这种情况下也没有意义).但请考虑以下示例:

Compiler says Type c does not conform to protocol 'p' and that's maybe because you can not use @objc optional and extensions at the same time (and does not make sence in this scenario either). But consider the following example:

我想在我的扩展中的协议中定义的非可选方法上设置一个选择器(我使用 @objc 的主要原因):

I want to set a selector on a non-optional method defined in protocol in my extension (main reason i used @objc):

func f1() { } -> func f1() { ... #selector(Self.f2) ... }

而且我还希望我的 f2() 函数具有默认行为.如果我把 f2() 标记为 optional,它就不能在 #selector 中使用,因为编译器不知道这个方法是否真的存在于需要的情况.当然有很多讨厌的解决方法,比如全局方法,将 Selector 发送到方法作为输入等等,但是有没有一种干净的方法来实现它?

And i also want my f2() function to have default behaviour. If i mark f2() as optional, it can not be used in #selector because compiler does not know if this method actually exists in the case of need. Sure there're lots of nasty workarounds like global methods, sending Selectors to methods as input and etc, but is there a clean way to achieve it?

这是实际问题

@objc
protocol Refreshable {
    weak var refreshControl: UIRefreshControl? { get set }
    optional func setupRefreshControl()
    func refresh()
}

@objc
protocol ContentLoader {
    func load(reset: Bool)
}

extension Refreshable where Self: ContentLoader {
    func refresh() {
        delay(0.75) { [weak self] in
            self?.load(true)
        }
    }
}

extension Refreshable where Self: UICollectionViewController {
    func setupRefreshControl() {
        let newRefreshControl = UIRefreshControl()

        newRefreshControl.tintColor = UIColor.grayColor()
        newRefreshControl.addTarget(self, action: #selector(Self.refresh), forControlEvents: .ValueChanged)
        collectionView?.addSubview(newRefreshControl)
        refreshControl = newRefreshControl
    }
}

现在如果一个 ViewController 实现了 RefreshableContentLoader,它不会找到默认的 refresh 函数,但它会找到 setupRefreshControl.所以我想让我们也将 refresh 标记为可选,但这样做,你不能再将它发送到选择器.

Now if a ViewController implements Refreshable and ContentLoader, it does not find the default refresh function, but it does find setupRefreshControl. So i figured let's mark refresh as optional too, but by doing that, you can not send it to selector any more.

我什至试过这个:

func refresh() -> 可选的func refresh()

let str = "refresh"
let sel = Selector(str)

它使编译器静音,是的,但也不起作用......上升无法识别的选择器发送到实例......

It silents the compiler yes, but does not work either... rises unrecognized selector sent to instance....

推荐答案

我认为这在 swift 中是不可能的(因为它桥接到 @objc 协议的方式).但这是一种解决方法(使用 Obj-c 关联对象)来解决 无法识别的选择器发送到实例... 问题.

I think this is not possible in swift (because of the way it bridges to @objc protocols). But this is a work around(using Obj-c associated objects) to solve the unrecognized selector sent to instance... problem.

fileprivate class AssociatedObject: NSObject {
    var closure: (() -> ())? = nil

    func trigger() {
        closure?()
    }
}

// Keys should be global variables, do not use, static variables inside classes or structs.
private var associatedObjectKey = "storedObject"

protocol CustomProtocol: class {
    func setup()
}

extension CustomProtocol where Self: NSObject {

    fileprivate var associatedObject: AssociatedObject? {
        get {
            return objc_getAssociatedObject(self, &associatedObjectKey) as? AssociatedObject
        }

        set {
            objc_setAssociatedObject(self, &associatedObjectKey, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }


    func setup() {
        let object = AssociatedObject()
        object.closure = { [weak self] in // Do not forget to use weak in order to avoid retain-cycle
            self?.functionToCallIndirectlyWithSelector()
        }

        let selector = #selector(object.trigger)
//      Uncomment next line to test it's functionality
        object.perform(selector)

//      Here, you must add selector to the target which needs to call the selector, for example:
//      refreshControl.addTarget(object, action: selector, forControlEvents: .valueChanged)

        self.associatedObject = object
    }

    func functionToCallIndirectlyWithSelector() {
        print("Function got called indirectly.")
    }
}


class CustomClass: NSObject, CustomProtocol {}

let instance = CustomClass()

instance.setup()

我添加了 Self: NSObject 约束以便能够在操场上测试它的功能,我不确定是否有必要.

I added Self: NSObject constraint to be able to test it's functionality in playground, I'm not sure if it's necessary or not.

这篇关于如何同时使用带有可选和扩展的@objc 协议?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆