441,swift函數(shù)參數(shù)指針傳遞inout和@escaping沖突的解決方法(面試點(diǎn):編譯器不允許逃逸閉包@escaping捕獲inout類型參數(shù),如果函數(shù)中沒有閉包,用inout可以修改值,...

我在swift的一個(gè)函數(shù)里面,參數(shù)里需要傳遞一個(gè)指針類型的Int,但是這個(gè)函數(shù)里面還有一個(gè)逃逸閉包@escaping,于是就會(huì)報(bào)錯(cuò)這樣:Escaping closure captures ‘inout’ parameter ‘selectRow’ ,意思是說:逃逸閉包補(bǔ)貨了一個(gè)inout參數(shù) selectRow

需求

我每次調(diào)用下面的swift方法,都會(huì)彈出一個(gè)PickerView,并且,設(shè)置label的text,同時(shí),記錄上次選中的selectRow

func setTimePicker2(selectRow:inout Int,label:UILabel){
        let vc = TDWPickerVC(items: timeItems, titleLabelStr: "請選擇時(shí)間段",selectRow: selectRow) {
            print("點(diǎn)擊取消")
        } confirmBlock: { (str,row) in
            print("點(diǎn)擊確定",str)
            selectRow = row
            label.text = str
        }
        print("time1VSelectRow=",selectRow )
        vc.modalPresentationStyle = .custom
        vc.modalTransitionStyle = .crossDissolve
        navigationController?.present(vc, animated: true, completion: nil)
    }

下面是調(diào)用的代碼

var time3VSelectRow = 0//類成員變量
setTimePicker2(selectRow: &time3VSelectRow, label: time3Label)//方法中的調(diào)用
``
再看控制器的定義里面有一個(gè)@escaping的逃逸閉包類型
```swift
  init(items:[String],titleLabelStr:String,selectRow:Int, cancleBlock:@escaping BlockVoid,confirmBlock:@escaping BlockStr) {
        self.titleLabelStr = titleLabelStr
        self.cancleBlock = cancleBlock
        self.confirmBlock = confirmBlock
        self.items = items
        self.selectRow = selectRow
        super.init(nibName: "TDWPickerVC", bundle: nil)
        
    }

錯(cuò)誤原因就是編譯器不允許逃逸閉包@escaping捕獲inout類型參數(shù),因?yàn)閕nout類型傳入的是一個(gè)指針,參數(shù)可以在外面更改,不安全

如果上面代碼沒有逃逸閉包,那么傳入一個(gè)inout Int 類型就沒問題了。setTimePicker2方法在執(zhí)行的時(shí)候同時(shí)更改selectRow這個(gè)Int數(shù)據(jù)

解決方法

創(chuàng)建一個(gè)類,用類包裹基礎(chǔ)數(shù)據(jù)類型Int這個(gè)類只用來存選中行數(shù),這個(gè)類只用來存選中行數(shù),因?yàn)橹苯佑没A(chǔ)數(shù)據(jù)類型inout int,不能在里面再調(diào)用逃逸閉包@escaping,但是參數(shù)傳入類的對象,是直接操作對象里面的成員,操作的地址相同。

class TimeVSelected:NSObject{
    var selectRow : Int = 0
}

函數(shù)定義成這樣,參數(shù)列表里面要類對象TimeVSelected,原來Int類型的地方用 selectobj.selectRow 對象的成員變量來代替

func setTimePicker(selectobj:  TimeVSelected,label:UILabel){
        //注意這里用selectobj.selectRow代替之前的selectRow
        let vc = TDWPickerVC(items: timeItems, titleLabelStr: "請選擇時(shí)間段",selectRow: selectobj.selectRow) {
            print("點(diǎn)擊取消")
        } confirmBlock: { (str,row) in
            print("點(diǎn)擊確定",str)
            selectobj.selectRow = row
            print("selectobj地址是:",selectobj)
            label.text = str
        }
        vc.modalPresentationStyle = .custom
        vc.modalTransitionStyle = .crossDissolve
        navigationController?.present(vc, animated: true, completion: nil)
    }

容易產(chǎn)生的問題

之前我也試過,既然類對象可以直接修改里面的對象,inout 和 @escaping不能一起用,那我為什么不能直接去掉inout這樣設(shè)置參數(shù)列表:

 func setTimePicker2(selectRow:Int,label:UILabel) 

這樣會(huì)報(bào)錯(cuò),因?yàn)楹瘮?shù)參數(shù)是不能在函數(shù)體里面改變的,函數(shù)參數(shù)被認(rèn)為是let常量,如下:報(bào)錯(cuò)

func setTimePicker2(selectRow:Int,label:UILabel){
       let vc = TDWPickerVC(items: timeItems, titleLabelStr: "請選擇時(shí)間段",selectRow: selectRow) {
           print("點(diǎn)擊取消")
       } confirmBlock: { (str,row) in
           print("點(diǎn)擊確定",str)
           selectRow = row //這里會(huì)報(bào)錯(cuò)Cannot assign to value: 'selectRow' is a 'let' constant
           label.text = str
       }
       print("time1VSelectRow=",selectRow )
       vc.modalPresentationStyle = .custom
       vc.modalTransitionStyle = .crossDissolve
       navigationController?.present(vc, animated: true, completion: nil)
   }

xcode截圖如下:

image.png

我們用類對象在函數(shù)體也不能改變,但是對象成員可以改變,看我下面的截圖,報(bào)錯(cuò)也是說對象是let類型不能改:

 func setTimePicker(selectobj:  TimeVSelected,label:UILabel){
        let vc = TDWPickerVC(items: timeItems, titleLabelStr: "請選擇時(shí)間段",selectRow: selectobj.selectRow) {
            print("點(diǎn)擊取消")
        } confirmBlock: { (str,row) in
            print("點(diǎn)擊確定",str)
            selectobj.selectRow = row
            selectobj = TimeVSelected()//這里就會(huì)報(bào)錯(cuò),因?yàn)閟electobj是函數(shù)參數(shù),不能賦值
            print("selectobj地址是:",selectobj)
            label.text = str
        }
        vc.modalPresentationStyle = .custom
        vc.modalTransitionStyle = .crossDissolve
        navigationController?.present(vc, animated: true, completion: nil)
    }
image.png

以前用C++的時(shí)候這種函數(shù)參數(shù)列表里的參數(shù)叫形參,是可以改的,但是改完以后,不會(huì)影響函數(shù)傳入之前的值,形參是臨時(shí)變量,函數(shù)結(jié)束就釋放了,swift就不一樣,類對象作為函數(shù)參數(shù),實(shí)際就是把類對象地址傳過來,跟c++一樣,改變對象里成員值,對象也被改變

print("timeVselected1地址是:",timeVselected1)

通過打印證明函數(shù)參數(shù)的類對象傳遞和C++的地址傳遞一樣,函數(shù)外和內(nèi)部都是一個(gè)地址

image.png

總結(jié)

以后再需要傳遞那種在函數(shù)里改變函數(shù)外的值,恰好函數(shù)內(nèi)部有逃逸閉包@escaping的,就用類對象傳遞到函數(shù)中,再改變類對象的成員,這樣就能在函數(shù)內(nèi)改變函數(shù)外的值了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容