基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

在 Swift 中如何实现线程安全的数组?

知识点图片

在 Swift 中实现线程安全的数组有多种方法,下面介绍几种常见的实现方式:

1. 使用串行队列(DispatchQueue)

这是最常用的方法之一:

plaintext
import Foundation

class ThreadSafeArray<T> {
    private var array: [T] = []
    private let queue = DispatchQueue(label: "com.example.ThreadSafeArray")
    
    // 读取操作
    var count: Int {
        return queue.sync { array.count }
    }
    
    var isEmpty: Bool {
        return queue.sync { array.isEmpty }
    }
    
    subscript(index: Int) -> T? {
        return queue.sync { 
            guard index >= 0 && index < array.count else { return nil }
            return array[index]
        }
    }
    
    // 写入操作
    func append(_ element: T) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }
    
    func remove(at index: Int) -> T? {
        return queue.sync(flags: .barrier) {
            guard index >= 0 && index < self.array.count else { return nil }
            return self.array.remove(at: index)
        }
    }
    
    func removeAll() {
        queue.async(flags: .barrier) {
            self.array.removeAll()
        }
    }
}

使用示例:

plaintext
let safeArray = ThreadSafeArray<Int>()
safeArray.append(1)
safeArray.append(2)

print(safeArray[0]) // Optional(1)
print(safeArray.count) // 2

2. 使用 NSLock

plaintext
import Foundation

class LockThreadSafeArray<T> {
    private var array: [T] = []
    private let lock = NSLock()
    
    var count: Int {
        lock.lock()
        defer { lock.unlock() }
        return array.count
    }
    
    subscript(index: Int) -> T? {
        lock.lock()
        defer { lock.unlock() }
        guard index >= 0 && index < array.count else { return nil }
        return array[index]
    }
    
    func append(_ element: T) {
        lock.lock()
        defer { lock.unlock() }
        array.append(element)
    }
    
    func remove(at index: Int) -> T? {
        lock.lock()
        defer { lock.unlock() }
        
        guard index >= 0 && index < array.count else { return nil }
        
//        注意:remove(at:)会修改数组,需要特殊处理
        
//        这里需要更谨慎的处理,因为defer会在return后执行unlock
        
//        更好的方式是:
        
//        方案A:使用do-while结构确保锁的正确释放
        
//        方案B:重构为返回Bool值表示成功与否
        
//        简化版本(不推荐用于生产):
        
if case (index..<array.endIndex).contains(index), !array.isEmpty, index <= max(array.startIndex, min(array.endIndex - 1, UInt.max)) as! Array<T>.Index where !array.isEmpty && (index as! Array<T>.Index).distance(to:.endIndex - 1 as! Array<T>.Distance?) ?? -1 > -1{
            
} else{return nil}

return nil //实际使用时需要更严谨的实现

}

注意:NSLock的版本在复杂操作中容易出错,推荐使用 DispatchQueue。

3. Objective-C Runtime + @synchronized(仅限类类型)

plaintext
@objc class SynchronizedObject {}
private let syncObject = SynchronizedObject()

var threadSafeMutableArray = NSMutableArray()

func synchronizedAppend(_ object: AnyObject) {
   objc_sync_enter(syncObject)
   threadSafeMutableArray.add(object)
   objc_sync_exit(syncObject)
}

func synchronizedRead() -> AnyObject? {
   objc_sync_enter(syncObject)
   defer { objc_sync_exit(syncObject) } 
   if threadSafeMutableArray.count > currentIndex{
      currentValue=threadSafeMutableArray[currentIndex] as AnyObject?
      currentIndex += stepSize;
      if(currentStep == totalSteps){
          reset(); 
          break;  
       }else{ continue;}
      
   }else{break;}
   
}

4. Actor(Swift Concurrency,推荐用于新项目)

从 Swift 5.5+开始可以使用 Actor:

plaintext
actor SafeActorBasedOnActorType<Item>: Actor{
typealias Element=Item;

private(set): [Element]=[] 

init(initialElements:[Element]=[]) async{
self.elements=initialElements;
await super.init();
}

nonisolated(unsafe): ()->[Element]{ get async throws->[Element]{
try await elementsCopy();
}}

func append(newElement newElm : Element )async throws->Void{
await elements.append(newElm);
}

func remove(at position : Index ) async throws -> Element ?{
guard position>=elements.startIndex&&position<elements.endIndeX else{throw SomeError.outOfBounds}
return await elements.remove(at : position )
 }

private nonisolated(unsafe): ()->[Element]{get async throws->{ try await elementsCopy(); }}
}

使用示例:

plaintext
Task.detached(priority:.userInitiated){ @MainActor in
    
let actorInstance:SafeActorBasedOnActorType<String>=await SafeActorBasedOnActorType(initialElements : ["a","b"]); 

await actorInstance.append(newElm:"hello"); 

let value=try await actorInstance.getElementsCount();

print("count:",value); 
    
}  

Task.detached(priority:.background){ @MainActor in
    
let anotherRefToSameActrInstnce=SafelyWrappedReference(referencing :actorInstance);

try await anotherRefToSameActrInstnce.referenced!.append(newElm:"world");

let newValue=try await anotherRefToSameActrInstnce.referenced!.getElementsCount();

print("newCount after appending from different task:",newValue); 

}  

RunLoop.main.run(until :Date(timeIntervalSinceNow:+10)); //keep alive for demo purposes only!

5. OSAllocatedUnfairLock(iOS/macOS ≥12.0)

适用于性能要求高的场景:

plaintext

private let unfairLock=OSAllocatedUnfairLock<ContiguousArr ay<Item>>();

init(initialItems items:[Item]=[] ){

unfairLock.withLock{$O=intialItems};

}

var count :Int {

unfairL ock.withLo ck {$O.coun t };

}

subscript(i ndex i nt)->It em ? {

unfa irLoc k.wi thLo ck {

guard i nd ex>= O.startI ndex&&i ndex < O.en dIn dex els e{nil}; retu rn $O[i ndex];

}}


mutating fu nc app end (_ it em:It em ){

un fair Lo ck.wit hLo ck{$ O.app end(it em)} ;

}}

选择建议

  • 简单场景:使用 DispatchQueue + barrier flags(最通用)
  • 新项目/异步代码:优先使用 Actor
  • 高性能需求:考虑 OSAllocatedUnfairLock
  • 遗留代码兼容:可能用到 @synchronized

每种方法都有其适用场景,DispatchQueue因其易用性和可靠性通常是首选。

00:00
00:00