LoginSignup
1
5

SwiftのStructの値渡し、write-back(≒ 参照渡し)、CoWの挙動確認

Last updated at Posted at 2021-08-29

結論

  • クロージャへは参照渡し、キャプチャリストを使えば値渡し
  • 関数へは値渡し、inoutを使えば 参照渡し write-back

Copy-on-Write (CoW)

  • SwiftのArrayやStringはパフォーマンスを考慮して、内部的にCoWが実装されている

確認

環境

  • Xcode 9.4
  • Swift 4.1

Playground

import Foundation

func address(of object: UnsafeRawPointer) -> String {
    let addr = Int(bitPattern: object)
    return String(format: "%p", addr)
}

func printLine(_ line: Int = #line) {
    print("#L\(line)")
}

print("--- Clousure: Int ---\n")
do {
    var value = 10
    
    let closure1 = {
        print("- value = \(value) (\(address(of: &value)))") // valueは参照渡し
    }
    
    let closure2 = { [value] in
        print("- value = \(value) ← 値渡しなので影響を受けない") // valueは値渡し
    }
    
    let closure3 = {
        value += 10
        print("- value = \(value) (\(address(of: &value)))") // valueは参照渡し
    }
    
    printLine()
    print("- value = \(value) (\(address(of: &value)))") // value = 10
    
    printLine()
    closure1() // value = 10
    
    printLine()
    value = 20
    print("- value = \(value) (\(address(of: &value)))")
    closure1() // value = 20 (参照渡しなので反映されている)
    
    printLine()
    closure2() // value = 10 (値渡しなので反映されていない)
    
    printLine()
    closure3() // value = 30
    
    printLine()
    print("- value = \(value) (\(address(of: &value)))") // value = 30 (closure3での変更が反映されている)
}

print("\n--- Clousure: [Int] ---\n")
do {
    var values = [10]
    
    let closure1 = {
        print("- values = \(values) (\(address(of: &values)))") // valueは参照渡し
    }
    
    let closure2 = { [values] in
        print("- values = \(values) ← 値渡しなので影響を受けない") // valueは値渡し
    }
    
    let closure3 = {
        values.append(values.last! + 10) // CoW
        print("- values = \(values) (\(address(of: &values))) ← CoWによりバッファへのポインタが変更されていることが確認できる") // valueは参照渡し
    }
    
    printLine()
    print("- values = \(values) (\(address(of: &values)))")
    
    printLine()
    closure1() // values = [10]
    
    printLine()
    values.append(20) // CoW
    print("- values = \(values) (\(address(of: &values))) ← CoWによりバッファへのポインタが変更されていることが確認できる") // values = [10, 20]
    closure1() // values = [10, 20] (参照渡しなので反映されている)
    
    printLine()
    closure2() // values = [10] (値渡しなので反映されていない)
    
    printLine()
    closure3() // value = [10, 20, 30]
    
    printLine()
    print("- values = \(values) (\(address(of: &values)))") // value = [10, 20, 30] (closure3での変更が反映)
}

print("\n--- Function: Int ---\n")
do {
    var value = 10
    
    func function1(_ value: inout Int) {
        print("- value = \(value) (\(address(of: &value)))") // valueは参照渡し
    }
    
    func function2( _ value: Int) {
        print("- value = \(value) ← 値渡しなので影響を受けない")  // valueは値渡し
    }
    
    func function3(_ value: inout Int) {
        value += 10
        print("- value = \(value) (\(address(of: &value)))") // valueは参照渡し
    }
    
    printLine()
    print("- value = \(value) (\(address(of: &value)))") // value = 10
    
    printLine()
    function1(&value) // value = 10
    
    printLine()
    value = 20
    print("- value = \(value) (\(address(of: &value)))") // value = 20
    
    printLine()
    function3(&value) // value = 30
    
    printLine()
    print("- value = \(value) (\(address(of: &value)))") // value = 30 (function3での変更が反映されている)
}

print("\n--- Function: [Int] ---\n")
do {
    var values = [10]
    
    func function1(_ values: inout [Int]) {
        print("- values = \(values) (\(address(of: &values)))") // valuesは参照渡し
    }
    
    func function2( _ values: [Int]) {
        print("- values = \(values) ← 値渡しなので影響を受けない")  // valuesは値渡し
    }
    
    func function3(_ values: inout [Int]) {
        values.append(values.last! + 10) // CoW
        print("- values = \(values) (\(address(of: &values))) ← CoWによりバッファへのポインタが変更されていることが確認できる") // valuesは参照渡し
    }
    
    printLine()
    print("- values = \(values) (\(address(of: &values)))") // values = [10]
    
    printLine()
    function1(&values) // values = [10]
    
    printLine()
    values.append(20) // CoW
    print("- values = \(values) (\(address(of: &values))) ← CoWによりバッファへのポインタが変更されていることが確認できる") // values = [10, 20]
    
    printLine()
    function3(&values) // value = [10, 20, 30]
    
    printLine()
    print("- values = \(values) (\(address(of: &values)))") // values = [10, 20, 30] (function3での変更が反映)
}

結果

--- Clousure: Int ---

#L31
- value = 10 (0x608000021d90)
#L34
- value = 10 (0x608000021d90)
#L37
- value = 20 (0x608000021d90)
- value = 20 (0x608000021d90)
#L42
- value = 10 ← 値渡しなので影響を受けない
#L45
- value = 30 (0x608000021d90)
#L48
- value = 30 (0x608000021d90)

--- Clousure: [Int] ---

#L69
- values = [10] (0x600000054ed0)
#L72
- values = [10] (0x600000054ed0)
#L75
- values = [10, 20] (0x60c0000553b0) ← CoWによりバッファへのポインタが変更されていることが確認できる
- values = [10, 20] (0x60c0000553b0)
#L80
- values = [10] ← 値渡しなので影響を受けない
#L83
- values = [10, 20, 30] (0x60c00006aba0) ← CoWによりバッファへのポインタが変更されていることが確認できる
#L86
- values = [10, 20, 30] (0x60c00006aba0)

--- Function: Int ---

#L107
- value = 10 (0x7ffeeaabede8)
#L110
- value = 10 (0x7ffeeaabede8)
#L113
- value = 20 (0x7ffeeaabede8)
#L117
- value = 30 (0x7ffeeaabede8)
#L120
- value = 30 (0x7ffeeaabede8)

--- Function: [Int] ---

#L141
- values = [10] (0x60c000056160)
#L144
- values = [10] (0x60c000056160)
#L147
- values = [10, 20] (0x604000055410) ← CoWによりバッファへのポインタが変更されていることが確認できる
#L151
- values = [10, 20, 30] (0x604000066b60) ← CoWによりバッファへのポインタが変更されていることが確認できる
#L154
- values = [10, 20, 30] (0x604000066b60)

参考

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5