閉包與代理逝嚎,編程內(nèi)功二者兼修

最近一個(gè)多月在瘋狂趕版本补君,寫了大量的代碼,用了大量的block伟桅。回過頭來看玖雁,我認(rèn)為自己過于依賴block了盖腕,于是以此文給后來人點(diǎn)建議和參考。

第一部分 局部作用域block

block本身是個(gè)好東西劲厌,它可以方便地將一段回調(diào)代碼當(dāng)成參數(shù)傳入一個(gè)方法补鼻,在只需要一個(gè)回調(diào)調(diào)用時(shí)尤其高效雅任,省去了定義class級(jí)別的protocol的繁瑣椿访。為調(diào)整坐標(biāo)代碼塊加動(dòng)畫虑润,統(tǒng)一時(shí)長(zhǎng),如下所示:


  這樣的block永遠(yuǎn)不存在內(nèi)存問題。因?yàn)閺膮?shù)傳入的block冗澈,在回調(diào)完后就釋放了亚亲,生命周期同局部變量。

第二部分 有管理器專門維護(hù)引用的block

    CZKLoadingView.showInContextView(AppDelegate.instance().window, withLoadingText: StrLoginning)
    RequestLogin().request(phone, passwd: passwd, type: LoginType.Phone, successCallback: { (code, msg) in
        CZKLoadingView.dismiss()
        if code == 0 {
            self.passwordTextField.text = ""
            NSNotificationCenter.defaultCenter().postNotificationName(NotificationLoginSuccessfully, object: self)
            delay(1) {
                self.navigationController?.popToRootViewControllerAnimated(false)
            }
        } else {
            if !isEmptyString(msg) {
                SVProgressHUD.showErrorWithStatus(msg)
            }
        }
        
        }, failureCallback: { (errorMsg) in
            CZKLoadingView.dismiss()
            SVProgressHUD.showErrorWithStatus(errorMsg)
    })

上面那段代碼,雖然請(qǐng)求是局部變量惜索,但請(qǐng)求會(huì)被加入到sessionManager的隊(duì)列中巾兆,請(qǐng)求完成才會(huì)被移出隊(duì)列虎囚。因此可以直接在block中使用self淘讥,生命周期不會(huì)出現(xiàn)循環(huán)引用的問題质帅。但下面這段代碼不同:

    weak var weakSelf = self
    SingleImagePickerAdapter.pickSingleImage(contextVC) { (image) in
        //獲取圖片后煤惩,進(jìn)入裁剪步驟
        let imageCropTool = ImageCrop()
        imageCropTool.delegate = weakSelf
        imageCropTool.ratioOfWidthAndHeight = 320.0 / 320.0
        imageCropTool.image = image
        imageCropTool.showWithAnimation(false)
    }

上述代碼的目的,是為了封裝請(qǐng)求一張圖片到拿到一張圖片的處理過程剪侮,那么問題來了洛退,先看需求兵怯。
  app需要支持從相冊(cè)選擇和拍照兩種方式獲取圖片,而這兩種方法都需要在發(fā)起操作后等待系統(tǒng)UIImagePickerController異步回調(diào)驼仪,回調(diào)中才能知道是拍照還是選擇相冊(cè)里的圖片袜漩,以及相冊(cè)訪問權(quán)限申請(qǐng)結(jié)果是成功還是失敗宙攻。如果在“我的”界面上傳頭像時(shí)寫一遍完整的UIImagePickerControllerDelegate實(shí)現(xiàn);然后相冊(cè)上傳時(shí)來一遍递惋;哈哈雹顺,還有聊天頁面發(fā)送圖片時(shí)嬉愧,再來一遍;什么王财,使用某些功能前還需要確保自己有頭像绒净,支持快捷上傳?
  聰明的軟件設(shè)計(jì)改览,在此處絕對(duì)需要祭出“封裝”這個(gè)大殺器缤言,把這些重復(fù)操作歸集到一個(gè)邏輯單元中,我將其命名為“adapter適配器”庆揩。上述代碼中的SingleImagePickerAdapter就是一個(gè)適配器订晌,發(fā)起操作后蚌吸,只需要接收一個(gè)回調(diào),回調(diào)中帶來產(chǎn)生的一張圖片推励,就可以方便的完成高內(nèi)聚低耦合、減少重復(fù)工作的目的稿黄。
  但是這里有個(gè)問題杆怕,adapter不像請(qǐng)求那樣,有專門的SessionManager去管理隊(duì)列中的請(qǐng)求寝杖,完成就移出隊(duì)列
互纯;adapter沒有任何更大的容器來引用它瑟幕,當(dāng)發(fā)起操作彈出UIImagePickerController后,局部變量adapter的生命周期就已經(jīng)結(jié)束了,它會(huì)立即釋放分配給它的內(nèi)存只盹,于是乎UIImagePickerController回調(diào)時(shí)辣往,接收回調(diào)的對(duì)象沒有了。
  這時(shí)有人就會(huì)說殖卑,你可以把a(bǔ)dapter作為實(shí)例變量聲明站削,哪里使用就在哪里保留強(qiáng)引用,像下面這樣寫:

//定義選取頭像adapter變量
private var adatper: SingleImagePickerAdapter?

//開始選取頭像的過程
func startHeadImagePickProcess(contextVC: CZKBaseViewController) {
    //打開照片選取來源提示框
    weak var weakSelf = self
    adatper = SingleImagePickerAdapter()
    adatper.pickSingleImage(contextVC) { (image) in
        weakSelf?.adatper = nil
        //獲取圖片后孵稽,進(jìn)入裁剪步驟
        let imageCropTool = ImageCrop()
        imageCropTool.delegate = weakSelf
        imageCropTool.ratioOfWidthAndHeight = 320.0 / 320.0
        imageCropTool.image = image
        imageCropTool.showWithAnimation(false)
    }
}

沒錯(cuò)许起,這樣確實(shí)可以避免問題菩鲜,但是使用時(shí)需要非常小心园细。第一,每次調(diào)用adapter的方法睦袖,都需要先聲明變量珊肃、管理adapter的生命周期,使用成本增加了馅笙;第二伦乔,block代碼塊中,一定不能出現(xiàn)self董习,因?yàn)閎lock如果對(duì)self有引用烈和,那adapter一定要先釋放,self才會(huì)釋放皿淋,一旦遺漏就會(huì)出現(xiàn)內(nèi)存泄露招刹。最后,我在不斷嘗試中窝趣,發(fā)現(xiàn)了一個(gè)絕佳的解決方案疯暑,外部代碼塊定義中可以隨便用self或weakSelf。這個(gè)方案的特點(diǎn)就是在adapter內(nèi)部做繁瑣的callback生命周期管理哑舒,但外部調(diào)用非常簡(jiǎn)單妇拯。就是在adapter類定義中,使用靜態(tài)變量引用當(dāng)前正在生效的adapter洗鸵,一旦不再需要這個(gè)adapter越锈,就置它為nil。設(shè)計(jì)思想類似于幾年前Objective-C中的MRC內(nèi)存管理原則膘滨。這樣一旦RunTime檢測(cè)到adapter對(duì)象不再有強(qiáng)引用指向它甘凭,就會(huì)立即釋放adatper,從而切斷adapter的callback對(duì)代碼塊中對(duì)象的強(qiáng)引用火邓。如下圖:

聲明類方法丹弱,支持快速發(fā)起獲取圖片操作德撬,并保持adapter引用
UIImagePickerController回調(diào)完成清空adapter

第三部分 多回調(diào)時(shí),代理協(xié)議更好

block既然這么好用蹈矮,那是不是任何情況都可以用block呢砰逻?當(dāng)然不是。試想一下泛鸟,如果一個(gè)管理器蝠咆,需要有很多回調(diào),這時(shí)block就沒法完成多回調(diào)的高效管理北滥。像tableView那樣多的回調(diào)方法刚操,最好的選擇還是代理協(xié)議。在現(xiàn)在的項(xiàng)目里再芋,也出現(xiàn)了很多回調(diào)方法的類菊霜,比如自定義控件,以及下面如圖所示的SNS平臺(tái)相關(guān)功能的回調(diào):

SNS管理器的回調(diào)

以上就是自己使用block的一些體會(huì)济赎。當(dāng)一個(gè)問題在量級(jí)小時(shí)也許怎么解決都不會(huì)有太大的負(fù)面代價(jià)鉴逞,但當(dāng)一段代碼、一種問題的解決過程司训,需要在很多地方使用時(shí)构捡,代碼的健壯性表露無遺,因此根據(jù)使用場(chǎng)景確定技術(shù)方案非常關(guān)鍵壳猜。
  沒有最好的技術(shù)勾徽,只有最合適的技術(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末统扳,一起剝皮案震驚了整個(gè)濱河市喘帚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咒钟,老刑警劉巖吹由,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異朱嘴,居然都是意外死亡溉知,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門腕够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舌劳,你說我怎么就攤上這事帚湘。” “怎么了甚淡?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵大诸,是天一觀的道長(zhǎng)捅厂。 經(jīng)常有香客問我,道長(zhǎng)资柔,這世上最難降的妖魔是什么焙贷? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮贿堰,結(jié)果婚禮上辙芍,老公的妹妹穿的比我還像新娘。我一直安慰自己羹与,他們只是感情好故硅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纵搁,像睡著了一般吃衅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腾誉,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天徘层,我揣著相機(jī)與錄音,去河邊找鬼利职。 笑死趣效,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眼耀。 我是一名探鬼主播英支,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哮伟!你這毒婦竟也來了抚太?” 一聲冷哼從身側(cè)響起炊林,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绘雁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡燃逻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年谈撒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碎税。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尤慰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雷蹂,到底是詐尸還是另有隱情伟端,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布匪煌,位于F島的核電站责蝠,受9級(jí)特大地震影響党巾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霜医,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一齿拂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肴敛,春花似錦署海、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昨登,卻和暖如春趾代,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丰辣。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工撒强, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笙什。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓飘哨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親琐凭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芽隆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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