Аномалия в CoW (Копирование при записи) Swift Array с элементами ссылочного типа - PullRequest
0 голосов
/ 03 мая 2020

Мое понимание:

Массивы в Swift являются типами значений. Массивы и другие коллекции в Swift имеют механизм CoW (Копировать при записи), поэтому, когда массив передается в качестве аргумента функции или просто присваивается другой переменной, Swift фактически не создает другую копию массива, а просто передает ссылку на тот же массив , При попытке записать / изменить массив, swift создаст новую копию массива (при условии, что ссылка на исходный массив все еще удерживается), и операция записи будет выполнена для новой копии массива.

Справочная информация:

В этой задаче я пытаюсь сохранить экземпляры классов (ссылочные типы в массиве)

class TestClass {
    var name: String = "abcd"
    init(name: String) {
        self.name = name
    }
}

Я создаю локальную переменную a (массив) TestClass и передав его в качестве аргумента someFunc

override func viewDidLoad() {
    super.viewDidLoad()
    var a = [TestClass(name: "abcd"), TestClass(name: "efgh"), TestClass(name: "ijkl")]
    debugPrint(UnsafePointer(&a))
    self.someFunc(array: a)
}

В someFunc я присваиваю аргумент другой переменной anotherArray и выполняю append операцию с anotherArray. Как и ожидалось, CoW of Array запускается и создает новую копию Array, поэтому адреса памяти array и anotherArray отличаются.

func someFunc(array: [TestClass]) {
    var anotherArray = array
    anotherArray.append(TestClass(name: "mnop"))

    debugPrint(UnsafePointer(&array))
    debugPrint(UnsafePointer(&anotherArray))
}

enter image description here

Как и ожидалось, при копировании типа значения все внутренние ссылочные типы также будут воссозданы / скопированы, чтобы доказать, что

  func someFunc(array: [TestClass]) {
    var anotherArray = array
    anotherArray.append(TestClass(name: "mnop"))

    for var value in array {
        debugPrint(UnsafePointer(&value))
    }

    for var value in anotherArray {
        debugPrint(UnsafePointer(&value))
    }
}

enter image description here

Очевидно, что адреса памяти массивов различны (array !=== anotherArray), а также адреса памяти всех элементов внутри array и anotherArray также различны (array[i] !=== anotherArray[i])

Проблема:

    func someFunc(array: [TestClass]) {
        var anotherArray = array
        anotherArray.append(TestClass(name: "mnop"))
        anotherArray[0].name = "Sandeep"
        debugPrint(array[0].name)
    }

При ясном понимании, что array и anotherArray - это две разные копии, а также ссылочные типы внутри каждого массива совершенно разные, можно было бы ожидать, если бы я изменил значение anotherArray[0].name на "Sandeep" , array[0].name все еще должно быть "abcd", но возвращает "Sandeep"

enter image description here

Почему это так? Я что-то здесь упускаю? Имеет ли это какое-либо отношение к Special Accessor Array mutableAddressWithPinnedNativeOwner?

Специальный метод доступа к массиву mutableAddressWithPinnedNativeOwner

Если я правильно понимаю, вместо того, чтобы просто извлекать значение по указанному c индексу, копировать его, изменять его и заменяя исходное значение, как в случае Dictionary , mutableAddressWithPinnedNativeOwner, просто обращайтесь к физической памяти значения по указанному c index и модифицируйте его. Но это не должно иметь значения, когда весь массив модифицируется: | Запутался здесь

Полный рабочий код:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        var a = [TestClass(name: "abcd"), TestClass(name: "efgh"), TestClass(name: "ijkl")]
        debugPrint(UnsafePointer(&a))
        self.someFunc(array: a)
        // Do any additional setup after loading the view.
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    func someFunc(array: [TestClass]) {
        var anotherArray = array
        anotherArray.append(TestClass(name: "mnop"))

        for var value in array {
            debugPrint(UnsafePointer(&value))
        }

        for var value in anotherArray {
            debugPrint(UnsafePointer(&value))
        }

        anotherArray[0].name = "Sandeep"
        debugPrint(array[0].name)
    }
}

1 Ответ

1 голос
/ 03 мая 2020

Похоже, UnsafePointer(&value) возвращает неправильное значение (возможно, это заголовок массива или что-то в этом роде). Я немного изменил someFunc.

func someFunc(array: [TestClass]) {
    var anotherArray = array
    anotherArray.append(TestClass(name: "mnop"))

    for var value in array {
        debugPrint(Unmanaged.passUnretained(value).toOpaque())
    }

    for var value in anotherArray {
        debugPrint(Unmanaged.passUnretained(value).toOpaque())
    }

    anotherArray[0].name = "Sandeep"
    debugPrint(array[0].name)
}

И получился следующий вывод:

0x0000600003f29360
0x0000600003f29380
0x0000600003f29400

0x0000600003f29360
0x0000600003f29380
0x0000600003f29400
0x0000600003f29340

Как видите, оба массива содержат одни и те же объекты, и это ожидаемое поведение. Массив хранит ссылки на TestClass объекты (не значения) и копирует эти ссылки во время CoW, но объекты остаются теми же.

...