Skip to content

Commit ad8e7f2

Browse files
committed
Fix Race conditions
1 parent ea24105 commit ad8e7f2

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

SDWebImageSwiftUI/Classes/ImageManager.swift

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,27 @@ public final class ImageManager : ObservableObject {
6161

6262
var currentURL: URL?
6363
var transaction = Transaction()
64-
var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
65-
var failureBlock: ((Error) -> Void)?
66-
var progressBlock: ((Int, Int) -> Void)?
64+
65+
// Thread-safe callback properties
66+
private let callbackQueue = DispatchQueue(label: "ImageManager.callbacks", qos: .userInitiated)
67+
private var _successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
68+
private var _failureBlock: ((Error) -> Void)?
69+
private var _progressBlock: ((Int, Int) -> Void)?
70+
71+
var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? {
72+
get { callbackQueue.sync { _successBlock } }
73+
set { callbackQueue.sync { _successBlock = newValue } }
74+
}
75+
76+
var failureBlock: ((Error) -> Void)? {
77+
get { callbackQueue.sync { _failureBlock } }
78+
set { callbackQueue.sync { _failureBlock = newValue } }
79+
}
80+
81+
var progressBlock: ((Int, Int) -> Void)? {
82+
get { callbackQueue.sync { _progressBlock } }
83+
set { callbackQueue.sync { _progressBlock = newValue } }
84+
}
6785

6886
public init() {}
6987

@@ -96,9 +114,11 @@ public final class ImageManager : ObservableObject {
96114
progress = 0
97115
}
98116
self.indicatorStatus.progress = progress
99-
if let progressBlock = self.progressBlock {
117+
// Capture progress callback in thread-safe way
118+
let progressCallback = self.progressBlock
119+
if let progressCallback = progressCallback {
100120
DispatchQueue.main.async {
101-
progressBlock(receivedSize, expectedSize)
121+
progressCallback(receivedSize, expectedSize)
102122
}
103123
}
104124
}) { [weak self] (image, data, error, cacheType, finished, _) in
@@ -112,6 +132,10 @@ public final class ImageManager : ObservableObject {
112132
// So previous View struct call `onDisappear` and cancel the currentOperation
113133
return
114134
}
135+
// Capture completion callbacks in thread-safe way
136+
let successCallback = self.successBlock
137+
let failureCallback = self.failureBlock
138+
115139
withTransaction(self.transaction) {
116140
self.image = image
117141
self.error = error
@@ -122,9 +146,9 @@ public final class ImageManager : ObservableObject {
122146
self.indicatorStatus.isLoading = false
123147
self.indicatorStatus.progress = 1
124148
if let image = image {
125-
self.successBlock?(image, data, cacheType)
149+
successCallback?(image, data, cacheType)
126150
} else {
127-
self.failureBlock?(error ?? NSError())
151+
failureCallback?(error ?? NSError())
128152
}
129153
}
130154
}

0 commit comments

Comments
 (0)