您好,登錄后才能下訂單哦!
NSArray 繼承自 NSObject,屬于對象,有 copy 方法。Swift 的 Array 是 struct,沒有 copy 方法。把一個 Array 變量賦值給另一個變量,兩個變量的內存地址相同嗎?與此相關的有多線程安全問題。本文探究這兩個問題。
定義測試 class 和 struct
class MyClass { var intArr = [Int]() var structArr = [MyStructElement]() var objectArr = [MyClassElement]() }struct MyStructElement {}class MyClassElement {}
定義輸出內存地址的 closure
let memoryAddress: (Any) -> String = { guard let cVarArg = $0 as? CVarArg else { return "Can not find memory address" } return String(format: "%p", cVarArg) }
private func testIntArr() { print(#function) let my = MyClass() for i in 0...10000 { my.intArr.append(i) } print("Before arr address:", memoryAddress(my.intArr)) // Copy Array is NOT thread safe let arr = my.intArr // If move this into async closure, crash print("Temp arr address:", memoryAddress(arr)) // Copy. Address different from my.intArr DispatchQueue.global().async { var sum = 0 for i in arr { sum += i } print("Sum:", sum) // 0 + 1 + ... + 10000 = 50005000 } my.intArr.removeAll() for _ in 0...10000 { my.intArr.append(0) } print("After arr address:", memoryAddress(my.intArr)) // New address}
在 view controller 中進行測試
override func viewDidLoad() { super.viewDidLoad() for _ in 0...1000 { testIntArr() } }
結果
Int array 的內存地址不同,賦值過程發生了 copy。
private func testStructArr() { print(#function) let my = MyClass() for _ in 0...10000 { my.structArr.append(MyStructElement()) } print("Before arr address:", memoryAddress(my.structArr)) // Copy Array is NOT thread safe let arr = my.structArr // If move this into async closure, crash print("Temp arr address:", memoryAddress(arr)) // Copy. Address different from my.structArr DispatchQueue.global().async { var sum = 0 for _ in arr { sum += 1 } print("Sum:", sum) // 10001 } my.structArr.removeAll() for _ in 0...10000 { my.structArr.append(MyStructElement()) } print("After arr address:", memoryAddress(my.structArr)) // New address}
在 view controller 中進行測試
override func viewDidLoad() { super.viewDidLoad() for _ in 0...1000 { testStructArr() } }
結果
Struct array 的內存地址不同,賦值過程發生了 copy。
private func testObjectArr() { print(#function) let my = MyClass() for _ in 0...10000 { my.objectArr.append(MyClassElement()) } print("Before arr address:", memoryAddress(my.objectArr)) // Copy Array is NOT thread safe let arr = my.objectArr // If move this into async closure, crash print("Temp arr address:", memoryAddress(arr)) // Not copy. Same as my.objectArr DispatchQueue.global().async { var sum = 0 for _ in arr { sum += 1 } print("Sum:", sum) // 10001 } my.objectArr.removeAll() for _ in 0...10000 { my.objectArr.append(MyClassElement()) } print("After arr address:", memoryAddress(my.objectArr)) // New address}
在 view controller 中進行測試
override func viewDidLoad() { super.viewDidLoad() for _ in 0...1000 { testObjectArr() } }
結果
一個 object array 變量賦值給另一個變量,兩個變量的內存地址相同,也就是說沒有 copy。原來的 array 改變后,內存地址改變,但不影響被賦值的變量。
以上的寫法是不會報錯的。如果把 array 的賦值寫入 async closure,就會報錯。多試幾次,會有不同的錯誤。
DispatchQueue.global().async { let arr = my.intArr // 在這里賦值會報錯 var sum = 0 for i in arr { sum += i } print("Sum:", sum) }
DispatchQueue.global().async { let arr = my.structArr // 在這里賦值會報錯 var sum = 0 for _ in arr { sum += 1 } print("Sum:", sum) }
DispatchQueue.global().async { let arr = my.objectArr // 在這里賦值會報錯 var sum = 0 for _ in arr { sum += 1 } print("Sum:", sum) }
對于 Int array 和 struct array 來說,賦值時進行了 copy,但這個步驟應該不是原子操作,所以放入 async closure 會出錯。對于 object array 來說,賦值過程雖然沒有進行 copy,但是要改變原來的 array 并且保持被賦值的對象不變,應該也要進行 copy;也就是說在更新 array 時才進行 copy。推測此時的 copy 也不是原子操作,所以放入 async closure 會出錯。
Array 的賦值過程是否進行 copy,與其中的元素類型有關。如果 array 的元素是 Int、struct 等,在賦值時就 copy。如果 array 的元素是 object,在賦值時不 copy,賦值后在更新其中一個 array 變量時才 copy。Array 的 copy 是線程不安全的。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。