非 - ' @ objc'方法不满足' @ objc'协议

时间:2016-09-14 09:37:59

标签: swift swift3 swift-protocols

概述:

  • 我有一个协议P1,它提供了一个Objective-C可选功能的默认实现。
  • 当我提供可选功能的默认实现时,会出现警告

编译器警告:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'

版本:

  • 斯威夫特:3
  • Xcode:8(公开发布)

尝试:

  • 尝试添加@objc,但没有帮助

问题:

  • 我如何解决这个问题?
  • 有工作吗?

代码:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {

}

extension P1 where Self : UIViewController {

    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}


class A : UIViewController, P1 {

}

3 个答案:

答案 0 :(得分:152)

虽然我认为我可以回答你的问题,但这不是你想要的答案。

TL; DR: @objc函数目前可能不在协议扩展中。你可以创建一个基类,虽然这不是一个理想的解决方案。

协议扩展和Objective-C

首先,这个问题/答案(Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c)似乎表明,由于协议扩展的方式被调度,协议扩展中声明的方法对于objc_msgSend()函数是不可见的,并且因此,Objective-C代码不可见。由于您尝试在扩展中定义的方法需要对Objective-C可见(因此UIKit可以使用它),它会因为不包括@objc而对您大喊大叫,但是一旦您确实包含它,它对你大喊大叫,因为协议扩展中不允许@objc。这可能是因为Objective-C当前无法看到协议扩展。

一旦我们添加@objc状态,我们也可以看到错误消息" @objc只能用于类成员,@ objc协议和类的具体扩展。"这不是一个阶级; @objc协议的扩展与协议定义本身(即需求中)的扩展不同,而且"具体的"会建议协议扩展不算作具体的类扩展。

解决方法

不幸的是,当Objective-C框架必须可以看到默认实现时,这几乎完全阻止了您使用协议扩展。起初,我想可能在你的协议扩展中不允许@objc,因为Swift编译器无法保证符合类型的类是类(即使你已经明确指定了UIViewController)。所以我对class提出了P1要求。这没用。

也许唯一的解决方法是在这里简单地使用基类而不是协议,但这显然不是完全理想的,因为类可能只有一个基类但符合多个协议。

如果您选择这条路线,请考虑此问题(Swift 3 ObjC Optional Protocol Method Not Called in Subclass)。似乎Swift 3中的另一个当前问题是子类不会自动继承其超类的可选协议要求实现。这些问题的答案使用@objc的特殊改编来绕过它。

报告问题

我认为在Swift开源项目中正在讨论这个问题,但是你可以确定他们使用Apple's Bug Reporter知道,这可能最终会进入Swift核心团队,或Swift's bug reporter。但是,这些中的任何一个都可能发现您的错误太广泛或已知。 Swift团队也可能会考虑您要求的新语言功能,在这种情况下,您应首先查看the mailing lists

更新

2016年12月,此问题was reported发送给Swift社区。该问题仍然标记为具有中等优先级的开放,但添加了以下注释:

  

这是有意的。没有办法将方法的实现添加到每个采用者,因为可以在符合协议之后添加扩展。我想如果扩展名与协议在同一模块中,我们可以允许它。

但是,由于您的协议与扩展程序位于同一模块中,因此您可以在将来的Swift版本中执行此操作。

更新2

2017年2月,此问题was officially closed为"胜利"由Swift核心团队成员之一提供以下消息:

  

这是故意的:由于Objective-C运行时的限制,协议扩展无法引入@objc入口点。如果要将@objc入口点添加到NSObject,请扩展NSObject。

扩展NSObject甚至UIViewController无法完全满足您的需求,但遗憾的是,它看起来不会成为可能。

在(非常)长期的未来,我们或许可以完全消除对@objc方法的依赖,但由于Cocoa框架目前不是用Swift编写的,因此很可能不会很快到来。直到它有稳定的ABI)。

答案 1 :(得分:0)

在我使用的快速框架中启用“模块稳定性”(打开“构建用于分发的库”)后,我遇到了这个问题。

我的经历是这样的:

    ds[,c("xnew","ynew","znew")] <- lapply(ds[,c("x","y","z")], function(x) x^2)

    Error in `[.data.frame`(ds, , c("x", "y", "z")) : 
    undefined columns selected

扩展名中的函数出现以下错误:

    “ LessAwesomeClass”的子类扩展中的
  • '@ objc'实例方法需要iOS 13.0.0

  • 非'@objc'方法'niceDelegateFunc'不满足'@objc'协议'GreatDelegate'的要求

将函数移至类而不是扩展中可以解决此问题。

答案 2 :(得分:0)

这是另一个解决方法。我也遇到了这个问题,还不能从UIKit切换到SwiftUI。对于我来说,也不可以将默认实现移植到通用基类中。我的默认实现非常广泛,所以我真的不想重复所有代码。我最终使用的解决方法是在协议中使用包装函数,然后从每个类中简单地调用这些函数。不太漂亮,但根据情况可能会比其他更好。您的代码将如下所示:

select distinct on (al.party_no) al.party_no, v.value
from assets.asset_latest al cross join lateral
     (values (al.mdl), (al.mfr_cd), (al.asset_id)
     ) v(value)
group by al.party_no, v.value
order by al.party_no, count(*) desc;