美文网首页iOS面试
关于In-Out关键字

关于In-Out关键字

作者: YYYYYY25 | 来源:发表于2019-02-09 00:45 被阅读6次

    对于Swift的值类型而言,当程序进行变量赋值、参数传递时,函数内部对值类型变量进行了拷贝。也就是说在函数体内无论对参数进行哪些修改,参数的本身都不会产生变化。

    所以,假如你需要通过函数直接交换两个变量的值,通常会这么做:

    func swapTwoInts(_ a: inout Int, _ b: inout Int) {
        let tempA = a
        a = b
        b = tempA
    }
    
    var a = 10, b = -10
    swapTwoInts(&a, &b)
    
    print(a,b) // a=-10 b=10
    

    我们之所以需要添加inout关键字,就是因为传入函数的变量是值类型,那么假如我传入的变量是个引用类型不就好了吗?

    class Data {
        var a = 0
        var b = 0
        init(a: Int, b: Int) {
            self.a = a
            self.b = b
        }
    }
    
    func swap(data: Data) {
        swapTwoInts(&data.a, &data.b)
    }
    
    var d = Data(a: 6, b: 9)
    swap(data: d)
    

    上面的代码同样可以交换a,b的值,因为对于Swift的引用类型,程序传递的是引用(指针),因此在函数体中对参数的修改会同时影响参数本身。

    也就是说,上面Data(a: 6, b: 9)对象的实例d和函数中形参data引用的是同一个对象。因此当函数执行,交换形参中a,b两个成员变量的值时,主程序中d对象对应的a,b也改变了。

    继续思考下面这段代码:

    func change(data: inout Data) {
        data = Data(a: 0, b: 1)
    }
    

    假如在上述函数中给data添加inout关键字修饰,然后在函数体创建另一个Data对象,会发生什么?

    答案显而易见,主程序中的d实例被改变了,这一点可以通过print(Unmanaged.passUnretained(d).toOpaque())打印变量的内存地址来验证。

    那么,假如程序做如下修改,会发生什么:

    func swap(data: inout Data!) {
        data = nil
        swapTwoInts(&data.a, &data.b)
    }
    
    var d: Data? = Data(a: 6, b: 9)
    swap(data: &d)
    

    答案是在swap(data: &d)这一行报错。因为主程序中的对象也会被修改为nil,导致程序运行时崩溃。

    最后,如果你用过早期的Swift版本,可能会见过变量形参这个东西,也就是在形参前使用var关键字修饰。那么如果代码改成下面这样,又会是什么结果呢?

    func swap(var data: Data) {
        data = nil
        swapTwoInts(&data.a, &data.b)
    }
    

    这段代码的运行是没有任何问题的,函数内部的data被赋值为nil,等于切断了data对堆中内容的指向,不形象外部变量。

    但是这种写法很容易误解,所以在之后的Swift版本中,移除了变量形参这个概念,其实他实际只是在函数内部创建一个新的变量而已。

    // 变量形参的实质
    func swap(data: Data) {
        var newData = data
        data = nil
        swapTwoInts(& newData.a, & newData.b)
    }
    

    你可能会疑问,引用类型传递到函数内,形参data和外部变量不是指向同一个对象吗?那么data=nil时为什么外部变量不发生变化呢?
    答:其实,方法内部data=nil只是将data变量的指针又原来的内存地址指向了nil,跟外部变量根本没关系。如果想要影响,则要添加inout关键字。

    总结:

    上面的内容其实没什么营养,我最早看到这个问题的时候觉得很有意思,因为涉及了值类型和引用类型的内存分布、In-Out参数等几个容易出错的知识点。但是写到最后好像并没有把我最初想表达的东西表现出来。
    其实此类问题可以引申的方向有很多,OC中block和基本数据类型、对象类型的关系,还有Swift中block的捕获等。以后有时间再整理吧。嘻嘻

    相关文章

      网友评论

        本文标题:关于In-Out关键字

        本文链接:https://www.haomeiwen.com/subject/jwxasqtx.html