设为首页 加入收藏

TOP

Swift Array copy 的线程安全问题(一)
2017-10-10 12:14:35 】 浏览:6272
Tags:Swift Array copy 线程 安全 问题

Swift Array copy 的线程安全问题

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)
}

测试 Int array

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。

测试 struct array

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。

测试 Object array

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,就会报错。多试几次,会有不同的错误。

Int array 的错误

DispatchQueue.global().async {
    let arr = my.intArr // 在这里赋值会报错
    var sum = 0
    for i in arr {
        sum += i
    }
    print("Sum:", sum)
}

Struct array 的错误

D
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Swift之父Chris Lattner将从Apple.. 下一篇Swift3.0服务端开发(五) 记事本的..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目