NSArray 继承自 NSObject,属于对象,有 copy 方法。Swift 的 Array 是 struct,没有 copy 方法。把一个 Array 变量赋值给另外一个变量,两个变量的内存地址相同吗?与此相关的有多线程安全问题。本文探究这两个问题。html
定义测试 class 和 structswift
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() } }
结果app
Int array 的内存地址不一样,赋值过程发生了 copy。async
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 中进行测试ide
override func viewDidLoad() { super.viewDidLoad() for _ in 0...1000 { testStructArr() } }
结果测试
Struct array 的内存地址不一样,赋值过程发生了 copy。this
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 中进行测试spa
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 是线程不安全的。
转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6288208.html