Irbisa · Шпаргалка
GCD и потоки
Очереди, QoS, DispatchGroup и семафоры, deadlock на main.sync, Operation, связь с async/await
Что такое
Thread(NSThread) и почему в iOS-приложениях потоки почти никогда не создают вручную?На каком потоке вызывается completion handler у
URLSessionи как из него обновить UI?Как выполнить код с задержкой через GCD и как отменить его до запуска?
final class SearchViewController: UIViewController { private var pendingSearch: DispatchWorkItem? func searchTextChanged(_ text: String) { pendingSearch?.cancel() // drops the previous item if it has not started let item = DispatchWorkItem { [weak self] in…Что такое Thread Sanitizer и чем он отличается от Main Thread Checker?
Что выведет этот код, если
run()вызван на главном потоке, и на каком потоке выполнится каждый блок?// run() is called on the main thread, for example from viewDidLoadfunc run() { let serial = DispatchQueue(label: "com.app.serial") DispatchQueue.main.async { print("A", Thread.isMainThread) } serial.sync { print("B", Thread.isMainThread) } DispatchQueue.global().sync { print("C", Thread.isMainThread) } serial.async { print("D", Thread.isMainThread) } serial.sync { print("E", Thread.isMainThread) } print("F")}Как barrier-блок превращает concurrent-очередь в reader-writer lock и когда флаг barrier ничего не делает?
final class SessionStore: @unchecked Sendable { private var tokens: [String: String] = [:] // right: a private concurrent queue. On DispatchQueue.global() the barrier flag is ignored private let queue = DispatchQueue(label: "com.app.sessions", attributes: .concurrent) func token(for user: String) -> String? {…Найдите баг: этот потокобезопасный кэш падает при первом же вызове
image(for:orInsert:).final class ImageCache: @unchecked Sendable { private let queue = DispatchQueue(label: "com.app.image-cache") private var storage: [URL: UIImage] = [:] func image(for url: URL) -> UIImage? { queue.sync { storage[url] } } func image(for url: URL, orInsert make: () -> UIImage) -> UIImage { queue.sync { if let cached = image(for: url) { return cached } let image = make() storage[url] = image return image } }}Найдите баг:
loadAvatarsиногда так и не вызывает completion.// api.fetchAvatar(id:completion:) is declared with a @MainActor completion, called on the main queue@MainActorfunc loadAvatars(ids: [String], completion: @escaping @MainActor ([UIImage]) -> Void) { let group = DispatchGroup() var images: [UIImage] = [] for id in ids { group.enter() api.fetchAvatar(id: id) { result in guard case .success(let image) = result else { return } images.append(image) group.leave() } } group.notify(queue: .main) { completion(images) }}Найдите баг: этот таймер повтора запускается из фоновой очереди и никогда не срабатывает.
final class UploadRetrier: @unchecked Sendable { private let queue = DispatchQueue(label: "com.app.upload") private var retryTimer: Timer? // Called by the upload code when a request fails func uploadDidFail() { queue.async { self.retryTimer?.invalidate() self.retryTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { [weak self] _ in self?.retry() } } } private func retry() { /* start the upload again */ }}Что делает
DispatchQueue.concurrentPerformи чем он лучшеglobal().asyncв цикле для тяжёлых вычислений?import Synchronizationimport Dispatch func checksum(_ chunk: ArraySlice<UInt8>) -> Int { chunk.reduce(0) { $0 &+ Int($1) } } func parallelChecksum(of bytes: [UInt8]) -> Int {…Какие локи вы используете в Swift (
NSLock,NSRecursiveLock,OSAllocatedUnfairLock,Mutex) и почему не голыйos_unfair_lock?import Foundationimport os // wrong: Swift does not guarantee a stable address for a stored struct, so &lock may be a// temporary copy; it often works by accidentfinal class UnsafeCounter: @unchecked Sendable {…Что из этого потокобезопасно в Swift:
static let,lazy var,Array, в который добавляют элементы, счётчикInt?Как выстроить цепочку работы через зависимости
Operationи что происходит с зависимой операцией, если её зависимость отменили?import Foundation final class ReadFileOperation: Operation, @unchecked Sendable { let url: URL private(set) var data: Data? init(url: URL) { self.url = url }…Когда в кодовой базе на Swift 6 и async/await GCD всё ещё остаётся правильным инструментом?
Как написать асинхронный подкласс
Operationи почему очередь застревает, еслиisFinishedне поддерживает KVO?import Foundation class AsyncOperation: Operation, @unchecked Sendable { private let lock = NSLock() private var _executing = false private var _finished = false…С каким QoS выполняется блок, отправленный через
async, как GCD решает проблему инверсии приоритетов и когда не может её решить?Instruments показывает 60+ потоков, потому что у каждого объекта своя serial-очередь. Почему так происходит и как это исправляют target-очереди?
// One serial queue per subsystem; every per-object queue funnels into itlet databaseQueue = DispatchQueue(label: "com.app.database", qos: .utility) final class Table: @unchecked Sendable { private let queue: DispatchQueue private var rows: [String] = []…Совпадает ли
DispatchQueue.main.asyncс выполнением на@MainActor, и что меняется при переводе GCD-кода на Swift Concurrency?nonisolated func didFetch(_ name: String, into label: UILabel) { // A closure passed literally to DispatchQueue.main.async is treated as @MainActor DispatchQueue.main.async { label.text = name } // Through a variable it is a plain @Sendable closure, and touching label is diagnosed: // let queue = DispatchQueue.main; queue.async { label.text = name }…