Skip to content

Commit ea8484d

Browse files
committed
URLSessionTask retain cycle fix
1 parent 9f2aff5 commit ea8484d

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

Foundation/URLSession/URLSessionTask.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ open class URLSessionTask : NSObject, NSCopying {
2929
internal var suspendCount = 1
3030
internal var session: URLSessionProtocol! //change to nil when task completes
3131
internal let body: _Body
32-
fileprivate var _protocol: URLProtocol! = nil
33-
32+
fileprivate var _protocol: URLProtocol? = nil
33+
3434
/// All operations must run on this queue.
3535
internal let workQueue: DispatchQueue
3636
/// Using dispatch semaphore to make public attributes thread safe.
@@ -200,8 +200,8 @@ open class URLSessionTask : NSObject, NSCopying {
200200
self.workQueue.async {
201201
let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil))
202202
self.error = urlError
203-
self._protocol.stopLoading()
204-
self._protocol.client?.urlProtocol(self._protocol, didFailWithError: urlError)
203+
self._protocol?.stopLoading()
204+
self._protocol?.client?.urlProtocol(self._protocol!, didFailWithError: urlError)
205205
}
206206
}
207207
}
@@ -263,7 +263,7 @@ open class URLSessionTask : NSObject, NSCopying {
263263

264264
if self.suspendCount == 1 {
265265
self.workQueue.async {
266-
self._protocol.stopLoading()
266+
self._protocol?.stopLoading()
267267
}
268268
}
269269
}
@@ -278,7 +278,21 @@ open class URLSessionTask : NSObject, NSCopying {
278278
self.updateTaskState()
279279
if self.suspendCount == 0 {
280280
self.workQueue.async {
281-
self._protocol.startLoading()
281+
if let _protocol = self._protocol {
282+
_protocol.startLoading()
283+
}
284+
else if self.error == nil {
285+
var userInfo: [String: Any] = [NSLocalizedDescriptionKey: "unsupported URL"]
286+
if let url = self.originalRequest?.url {
287+
userInfo[NSURLErrorFailingURLErrorKey] = url
288+
userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString
289+
}
290+
let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain,
291+
code: NSURLErrorUnsupportedURL,
292+
userInfo: userInfo))
293+
self.error = urlError
294+
_ProtocolClient().urlProtocol(task: self, didFailWithError: urlError)
295+
}
282296
}
283297
}
284298
}
@@ -573,6 +587,7 @@ extension _ProtocolClient : URLProtocolClient {
573587
session.taskRegistry.remove(task)
574588
}
575589
}
590+
task._protocol = nil
576591
}
577592

578593
func urlProtocol(_ protocol: URLProtocol, didCancel challenge: URLAuthenticationChallenge) {
@@ -600,6 +615,10 @@ extension _ProtocolClient : URLProtocolClient {
600615

601616
func urlProtocol(_ protocol: URLProtocol, didFailWithError error: Error) {
602617
guard let task = `protocol`.task else { fatalError() }
618+
urlProtocol(task: task, didFailWithError: error)
619+
}
620+
621+
func urlProtocol(task: URLSessionTask, didFailWithError error: Error) {
603622
guard let session = task.session as? URLSession else { fatalError() }
604623
switch session.behaviour(for: task) {
605624
case .taskDelegate(let delegate):
@@ -624,6 +643,7 @@ extension _ProtocolClient : URLProtocolClient {
624643
session.taskRegistry.remove(task)
625644
}
626645
}
646+
task._protocol = nil
627647
}
628648

629649
func urlProtocol(_ protocol: URLProtocol, cachedResponseIsValid cachedResponse: CachedURLResponse) {

Foundation/URLSession/http/HTTPURLProtocol.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,21 @@ import Dispatch
1313
internal class _HTTPURLProtocol: URLProtocol {
1414

1515
fileprivate var easyHandle: _EasyHandle!
16-
fileprivate var tempFileURL: URL
16+
fileprivate lazy var tempFileURL: URL = {
17+
let fileName = NSTemporaryDirectory() + NSUUID().uuidString + ".tmp"
18+
_ = FileManager.default.createFile(atPath: fileName, contents: nil)
19+
return URL(fileURLWithPath: fileName)
20+
}()
1721

1822
public required init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
1923
self.internalState = _InternalState.initial
20-
let fileName = NSTemporaryDirectory() + NSUUID().uuidString + ".tmp"
21-
_ = FileManager.default.createFile(atPath: fileName, contents: nil)
22-
self.tempFileURL = URL(fileURLWithPath: fileName)
2324
super.init(request: task.originalRequest!, cachedResponse: cachedResponse, client: client)
2425
self.task = task
2526
self.easyHandle = _EasyHandle(delegate: self)
2627
}
2728

2829
public required init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
2930
self.internalState = _InternalState.initial
30-
let fileName = NSTemporaryDirectory() + NSUUID().uuidString + ".tmp"
31-
_ = FileManager.default.createFile(atPath: fileName, contents: nil)
32-
self.tempFileURL = URL(fileURLWithPath: fileName)
3331
super.init(request: request, cachedResponse: cachedResponse, client: client)
3432
self.easyHandle = _EasyHandle(delegate: self)
3533
}

0 commit comments

Comments
 (0)