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