iOS开发者在Swift中应避免过度使用@objc Avoiding the overuse of @objc

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看120 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 120|回复: 1
收起左侧

iOS开发者在Swift中应避免过度使用@objc Avoiding the overuse of @objc

[复制链接]
cocoaswift 发表于 2016-6-28 11:00:58 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码

就在前几天,我终于把项目迁移到了Swift2.2,在使用SE-0022建议的#selector语句时,我遇到了一些问题。如果在protocol extension中使用#selector,这个protocol必须添加@Objc修饰符。而之前的Selector("method:")语句则不需要添加。

iOS开发者在Swift中应避免过度使用@objc Avoiding the overuse of @objc

iOS开发者在Swift中应避免过度使用@objc Avoiding the overuse of @objc - 敏捷大拇指 - iOS开发者在Swift中应避免过度使用@objc Avoiding the overuse of @objc





1、通过协议的扩展配置视图控制器

为了达到本文的目的,我简化了工作中项目的代码,但所有核心的思想都保留着。一种我经常在Swift里用的模式是:为了重用的配置写protocols(协议)和extensions(扩展),特别是有Uikit的时候。

假设我们有一组视图控制器,每个控制器都需要一个 view model 和 一个“取消”按钮。每一个控制器需要各自响应“cancel”按钮的点击事件。我们可以这样写:

[Swift] 纯文本查看 复制代码
struct ViewModel {
    let title: String
}

protocol ViewControllerType: class {
    var viewModel: ViewModel { get set }

    func didTapCancelButton(sender: UIBarButtonItem)
}


如果就写成这样,那每个控制器都需要自己去添加和写一个一样的取消按钮。这样就会有很多一样的代码。我们可以通过扩展(用老的 Selector("") 语句)来解决:

[Swift] 纯文本查看 复制代码
extension ViewControllerType where Self: UIViewController {
    func configureNavigationItem() {
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .Cancel,
            target: self,
            action: Selector("didTapCancelButton:"))
    }
}


现在每个符合协议的控制器都可以通过在viewDidLoad()里调用协议的configureNavigationItem() 方法来配置取消按钮,是不是好多了~我们的控制器看起来是这样的:

[Swift] 纯文本查看 复制代码
class MyViewController: UIViewController, ViewControllerType {
    var viewModel = ViewModel(title: "Title")

    override func viewDidLoad() {
        super.viewDidLoad()
        configureNavigationItem()
    }

    func didTapCancelButton(sender: UIBarButtonItem) {
        // handle tap
    }
}


这仅是一个简单的例子,但我们可以想象通过这个方式制造更多复杂的配置。

把以上代码段升级到 Swift 2.2后,是这样的:

[Swift] 纯文本查看 复制代码
extension ViewControllerType where Self: UIViewController {
    func configureNavigationItem() {
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .Cancel,
            target: self,
            action: #selector(didTapCancelButton(_:)))
    }
}


但现在我们有了个问题,一个新的编译错误:

[Swift] 纯文本查看 复制代码
Argument of '#selector' refers to a method that is not exposed to Objective-C.

Fix-it   Add '@objc' to expose this method to Objective-C





2、当@objc试图破坏所有的东西

因为一系列的原因, 在原始的ViewControllerType协议中,我们并不能简单的给这个方法添加一个@objc修饰符。如果我们这么做了,那么所有的protocol都需要用@objc来标记,这将意味着:

  • 所有这个protocol的父protocol都需要用@objc来标记。
  • 所有继承自这个protocol的protocol都会被自动添加@objc。
  • 我们在protocol中的结构体(ViewModel)不能用Objective-C来表示。


到目前,@objc在这里的唯一功能就是定义了一个普通的target-action selectors。尽管我们可以使用swift的强大功能,但是因为Cocoa依然贯穿我们的代码Cocoa all the way down,我们并没有正真的在写纯粹的swift - 除非我们开始在各个地方引入@objc。

我们在这的例子很简单,但是想象一下更复杂的类依赖关系图,大量使用Swift的值类型和当这个协议处在多个协议的中间层时。把引入@objc作为解决方案真是app的末日。如果我们这样做,@objc这种做法会让我们的Swift代码毫无美感并变得乱糟糟。这会毁了所有的东西。

但是希望还是有的。




3、不使用@objc来避免乱糟糟

我们大可不必让为了让我们的Swifit代码能使用Objcetive-C的语法而使用@objc。

我们可以把protocol分解成多个protocol来去除@objc,然后我们再重组这些protocol。事实上,我们可以让编译器顺利编译和避免更改任何视图控制器的代码。

第一步,我们把protocol拆成2个。ViewModelConfigurable 和 NavigationItemConfigurable。把ViewControllerType里的extension放到NavigationItemConfigurable。

[Swift] 纯文本查看 复制代码
protocol ViewModelConfigurable {
    var viewModel: ViewModel { get set }
}

@objc protocol NavigationItemConfigurable: class {
    func didTapCancelButton(sender: UIBarButtonItem)
}


最终,我们可以把原ViewControllerType protocol定义成typealias。

[Swift] 纯文本查看 复制代码
typealias ViewControllerType = protocol<ViewModelConfigurable, NavigationItemConfigurable>


和迁移到Swift2.2之前比一切都很正常,而且我们定义的原视图控制器也没有发生任何改变,没有东西被破坏。如果你曾经遇到类似的情况,或者你也想阻止@objc带来的破坏(你应该这么做),我强烈建议采用这个策略。




4、这并不是显而易见的

现在的代码,我还是觉得有点不爽,当然,针对这个问题,这就是最Swift化的答案。当Xcode突然开始提示你并且很快的应用它的修复方案依然会把所有都破坏掉。特别是当Xcode提供的修复方案正中你下怀的时候,这个时候,上面说的到的这类解决方案并不能立即很清楚。

最后,在做了以上那些更改之后,我意识到总的来说这其实是一个很好的解决方案。。没有什么理由在一个地方只用一个协议。像ViewModelConfigurable 和 NavigationItemConfigurable这两个协议分工明确。把不同的协议组合在一起始终都是最优雅、最适当的设计。





原文链接 : Avoiding the overuse of @objc in Swift
原文作者 : Jesse Squires
译文出自 : 掘金翻译计划
译者 : Dwight
校对者: jk77me, owenlyn

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 收起 理由
Anewczs + 10 推送上首页了~分享到朋友圈微博QQ空间吧~.

查看全部评分

小开开 发表于 2016-6-30 11:33:44 | 显示全部楼层
还有一个就是Swift版本监测的依赖症
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表