[譯]iOS開(kāi)發(fā)者在Swift中應(yīng)避免過(guò)度使用@objc

就在前幾天蠢护,我終于把項(xiàng)目遷移到了Swift2.2,在使用SE-0022建議的#selector語(yǔ)句時(shí)养涮,我遇到了一些問(wèn)題葵硕。如果在protocol extension中使用#selector,這個(gè)protocol必須添加@Objc修飾符贯吓。而之前的Selector("method:")語(yǔ)句則不需要添加懈凹。

通過(guò)協(xié)議的擴(kuò)展配置視圖控制器

為了達(dá)到本文的目的,我簡(jiǎn)化了工作中項(xiàng)目的代碼悄谐,但所有核心的思想都保留著介评。一種我經(jīng)常在swift里用的模式是:為了重用的配置寫(xiě)protocols(協(xié)議)和extensions(擴(kuò)展),特別是有Uikit的時(shí)候

假設(shè)我們有一組視圖控制器爬舰,每個(gè)控制器都需要一個(gè) view model 和 一個(gè)“取消”按鈕威沫。每一個(gè)控制器需要各自響應(yīng)
“cancel”按鈕的點(diǎn)擊事件。我們可以這樣寫(xiě):

struct ViewModel {
    let title: String
}

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

    func didTapCancelButton(sender: UIBarButtonItem)
}

如果就寫(xiě)成這樣洼专,那每個(gè)控制器都需要自己去添加和寫(xiě)一個(gè)一樣的取消按鈕棒掠。這樣就會(huì)有很多一樣的代碼。我們可以通過(guò)擴(kuò)展(用老的 Selector("") 語(yǔ)句)來(lái)解決:

extension ViewControllerType where Self: UIViewController {
    func configureNavigationItem() {
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .Cancel,
            target: self,
            action: Selector("didTapCancelButton:"))
    }
}

現(xiàn)在每個(gè)符合協(xié)議的控制器都可以通過(guò)在viewDidLoad()里調(diào)用協(xié)議的configureNavigationItem() 方法來(lái)配置取消按鈕屁商,是不是好多了~我們的控制器看起來(lái)是這樣的:

class MyViewController: UIViewController, ViewControllerType {
    var viewModel = ViewModel(title: "Title")

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

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

這僅是一個(gè)簡(jiǎn)單的例子烟很,但我們可以想象通過(guò)這個(gè)方式制造更多復(fù)雜的配置。

把以上代碼段升級(jí)到 Swift 2.2后蜡镶,是這樣的:

extension ViewControllerType where Self: UIViewController {
    func configureNavigationItem() {
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .Cancel,
            target: self,
            action: #selector(didTapCancelButton(_:)))
    }
}

但現(xiàn)在我們有了個(gè)問(wèn)題雾袱,一個(gè)新的編譯錯(cuò)誤

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

當(dāng)@objc試圖破壞所有的東西

因?yàn)橐幌盗械脑颍?在原始的ViewControllerType協(xié)議中,我們并不能簡(jiǎn)單的給這個(gè)方法添加一個(gè)@objc修飾符官还。如果我們這么做了芹橡,那么所有的protocol都需要用@objc來(lái)標(biāo)記,這將意味著:

  • 所有這個(gè)protocol的父protocol都需要用@objc來(lái)標(biāo)記望伦。
  • 所有繼承自這個(gè)protocol的protocol都會(huì)被自動(dòng)添加@objc林说。
  • 我們?cè)趐rotocol中的結(jié)構(gòu)體(ViewModel)不能用Objective-C來(lái)表示。

到目前屯伞,@objc在這里的唯一功能就是定義了一個(gè)普通的target-action selectors腿箩。盡管我們可以使用swift的強(qiáng)大功能,但是因?yàn)镃ocoa依然貫穿我們的代碼Cocoa all the way down劣摇,我們并沒(méi)有正真的在寫(xiě)純粹的swift - 除非我們開(kāi)始在各個(gè)地方引入@objc珠移。

我們?cè)谶@的例子很簡(jiǎn)單,但是想象一下更復(fù)雜的類(lèi)依賴關(guān)系圖,大量使用Swift的值類(lèi)型和當(dāng)這個(gè)協(xié)議處在多個(gè)協(xié)議的中間層時(shí)钧惧。把引入@objc作為解決方案真是app的末日暇韧。如果我們這樣做,@objc這種做法會(huì)讓我們的Swift代碼毫無(wú)美感并變得亂糟糟浓瞪。這會(huì)毀了所有的東西锨咙。

但是希望還是有的。

不使用@objc來(lái)避免亂糟糟

我們大可不必讓為了讓我們的Swifit代碼能使用Objcetive-C的語(yǔ)法而使用@objc追逮。

我們可以把protocol分解成多個(gè)protocol來(lái)去除@objc酪刀,然后我們?cè)僦亟M這些protocol。事實(shí)上钮孵,我們可以讓編譯器順利編譯和避免更改任何視圖控制器的代碼骂倘。

第一步,我們把protocol拆成2個(gè)巴席。ViewModelConfigurableNavigationItemConfigurable历涝。把ViewControllerType里的extension放到NavigationItemConfigurable

protocol ViewModelConfigurable {
    var viewModel: ViewModel { get set }
}

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

最終漾唉,我們可以把原ViewControllerType protocol定義成typealias荧库。

typealias ViewControllerType = protocol<ViewModelConfigurable, NavigationItemConfigurable>

和遷移到Swift2.2之前比一切都很正常,而且我們定義的原視圖控制器也沒(méi)有發(fā)生任何改變赵刑,沒(méi)有東西被破壞分衫。如果你曾經(jīng)遇到類(lèi)似的情況,或者你也想阻止@objc帶來(lái)的破壞(你應(yīng)該這么做)般此,我強(qiáng)烈建議采用這個(gè)策略蚪战。

這并不是顯而易見(jiàn)的

現(xiàn)在的代碼,我還是覺(jué)得有點(diǎn)不爽铐懊,當(dāng)然邀桑,針對(duì)這個(gè)問(wèn)題,這就是最Swift化的答案科乎。當(dāng)Xcode突然開(kāi)始提示你并且很快的應(yīng)用它的修復(fù)方案依然會(huì)把所有都破壞掉惫谤。特別是當(dāng)Xcode提供的修復(fù)方案正中你下懷的時(shí)候杠河,這個(gè)時(shí)候拾因,上面說(shuō)的到的這類(lèi)解決方案并不能立即很清楚罢维。

最后,在做了以上那些更改之后玉吁,我意識(shí)到總的來(lái)說(shuō)這其實(shí)是一個(gè)很好的解決方案照弥。。沒(méi)有什么理由在一個(gè)地方只用一個(gè)協(xié)議进副。像ViewModelConfigurableNavigationItemConfigurable這兩個(gè)協(xié)議分工明確。把不同的協(xié)議組合在一起始終都是最優(yōu)雅、最適當(dāng)?shù)脑O(shè)計(jì)影斑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末给赞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子矫户,更是在濱河造成了極大的恐慌片迅,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皆辽,死亡現(xiàn)場(chǎng)離奇詭異柑蛇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)驱闷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)耻台,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人空另,你說(shuō)我怎么就攤上這事盆耽。” “怎么了扼菠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵摄杂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我循榆,道長(zhǎng)析恢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任秧饮,我火速辦了婚禮氮昧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浦楣。我一直安慰自己袖肥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布振劳。 她就那樣靜靜地躺著椎组,像睡著了一般。 火紅的嫁衣襯著肌膚如雪历恐。 梳的紋絲不亂的頭發(fā)上寸癌,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音弱贼,去河邊找鬼蒸苇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吮旅,可吹牛的內(nèi)容都是我干的溪烤。 我是一名探鬼主播味咳,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼檬嘀!你這毒婦竟也來(lái)了槽驶?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸳兽,失蹤者是張志新(化名)和其女友劉穎掂铐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體揍异,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡全陨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衷掷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辱姨。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖棍鳖,靈堂內(nèi)的尸體忽然破棺而出炮叶,到底是詐尸還是另有隱情,我是刑警寧澤渡处,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布镜悉,位于F島的核電站,受9級(jí)特大地震影響医瘫,放射性物質(zhì)發(fā)生泄漏侣肄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一醇份、第九天 我趴在偏房一處隱蔽的房頂上張望稼锅。 院中可真熱鬧,春花似錦僚纷、人聲如沸矩距。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锥债。三九已至,卻和暖如春痊臭,著一層夾襖步出監(jiān)牢的瞬間哮肚,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工广匙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留允趟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓鸦致,卻偏偏與公主長(zhǎng)得像潮剪,于是被迫代替她去往敵國(guó)和親涣楷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 暑期帶小兒學(xué)習(xí)經(jīng)典鲁纠,重讀《詩(shī)經(jīng)》总棵△⒓牛“桃之夭夭改含,灼灼其華;之子于歸迄汛,宜其室家捍壤。”面若桃花的女子鞍爱,帶著青春靚麗的...
    水心水心閱讀 309評(píng)論 0 1
  • 這是一種愉悅的體驗(yàn)鹃觉。首先,不得不承認(rèn)自己接受信息的途徑比較單一睹逃,一方面只接受自己想接受的信息盗扇;另一方面只接受自己想...
    Daisy向陽(yáng)花兒閱讀 977評(píng)論 0 0
  • 人生有時(shí)需要激情疗隶,一場(chǎng)說(shuō)走就走的旅行足以證明這一切。機(jī)關(guān)事業(yè)單位工作確實(shí)很枯燥翼闹,對(duì)于我這樣一個(gè)隨性的人而已斑鼻,一...
    凌峰style閱讀 591評(píng)論 0 2
  • 授銜,和陸毫攒空將軍的合影坚弱。
    孟慶江閱讀 281評(píng)論 0 0