Skip to content

Commit

Permalink
Fix watchOS Memory Leaks (Alamofire#3110)
Browse files Browse the repository at this point in the history
* Add watchOS sample project for testing.

* Always disassociate on watchOS.

* Add information comment about watchOS workaround.

* Clean up the watchOS example.

* Add bug and workarounds to README and documentation.

* Add proper licensing headers.

* Delay completion of request for test stability.
  • Loading branch information
jshier authored Mar 15, 2020
1 parent e02ea43 commit cc54c25
Show file tree
Hide file tree
Showing 28 changed files with 1,280 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Alamofire.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Documentation/AdvancedUsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ AF.request(...)
}
```

> Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.

### `DataRequest`
`DataRequest` is a subclass of `Request` which encapsulates a `URLSessionDataTask` downloading a server response into `Data` stored in memory. Therefore, it’s important to realize that extremely large downloads may adversely affect system performance. For those types of downloads, using `DownloadRequest` to save the data to disk is recommended.

Expand Down
2 changes: 2 additions & 0 deletions Documentation/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,8 @@ AF.request("https://httpbin.org/get").responseJSON { response in
}
```

> Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.

### cURL Command Output

Debugging platform issues can be frustrating. Thankfully, Alamofire's `Request` type can produce the equivalent cURL command for easy debugging. Due to the asynchronous nature of Alamofire's `Request` creation, this API has both synchronous and asynchronous versions. To get the cURL command as soon as possible, you can chain the `cURLDescription` onto a request:
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ The following radars have some effect on the current implementation of Alamofire
- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case
- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS

## Resolved Radars

Expand All @@ -161,7 +162,12 @@ The following radars have been resolved over time after being filed against the
- (Resolved): 9/1/17 in Xcode 9 beta 6.
- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+
- (Resolved): Just add `CFNetwork` to your linked frameworks.


## Workarounds

- Collection of `URLSessionTaskMetrics` is currently disabled on watchOS due to `FB7624529`.
- `-no_compact_unwind` linker warning is currently disabled on watchOS due to the persistent existence of the warning.

## FAQ

### What's the origin of the name Alamofire?
Expand Down
4 changes: 4 additions & 0 deletions Source/RequestTaskMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,12 @@ struct RequestTaskMap {

switch (events.completed, events.metricsGathered) {
case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
#if os(watchOS) // watchOS doesn't gather metrics, so unconditionally remove the reference and return true.
default: self[task] = nil; return true
#else
case (false, false): taskEvents[task] = (completed: true, metricsGathered: false); return false
case (false, true): self[task] = nil; return true
#endif
}
}
}
6 changes: 6 additions & 0 deletions Source/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public struct DataResponse<Success, Failure: Error> {
public let data: Data?

/// The final metrics of the response.
///
/// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.`
///
public let metrics: URLSessionTaskMetrics?

/// The time taken to serialize the response.
Expand Down Expand Up @@ -224,6 +227,9 @@ public struct DownloadResponse<Success, Failure: Error> {
public let resumeData: Data?

/// The final metrics of the response.
///
/// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.`
///
public let metrics: URLSessionTaskMetrics?

/// The time taken to serialize the response.
Expand Down
10 changes: 10 additions & 0 deletions Tests/SessionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,16 @@ final class SessionCancellationTestCase: BaseTestCase {
final class OnceRetrier: RequestInterceptor {
private var hasRetried = false

func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
if hasRetried {
var request = urlRequest
request.url = URL.makeHTTPBinURL(path: "delay/1")
completion(.success(request))
} else {
completion(.success(urlRequest))
}
}

func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
completion(hasRetried ? .doNotRetry : .retry)
hasRetried = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "40mm"
},
{
"size" : "50x50",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "44mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
},
{
"size" : "108x108",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "44mm"
},
{
"idiom" : "watch-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="11134" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="3mp-fW-waa">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="11055"/>
</dependencies>
<scenes>
<!--Interface Controller-->
<scene sceneID="aou-V4-d1y">
<objects>
<hostingController id="3mp-fW-waa" customClass="HostingController"
customModuleProvider="target"/>
</objects>
</scene>
</scenes>
</document>
31 changes: 31 additions & 0 deletions watchOS Example/watchOS Example WatchKit App/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>watchOS Example WatchKit App</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"assets" : [
{
"idiom" : "watch",
"filename" : "Circular.imageset",
"role" : "circular"
},
{
"idiom" : "watch",
"filename" : "Extra Large.imageset",
"role" : "extra-large"
},
{
"idiom" : "watch",
"filename" : "Graphic Bezel.imageset",
"role" : "graphic-bezel"
},
{
"idiom" : "watch",
"filename" : "Graphic Circular.imageset",
"role" : "graphic-circular"
},
{
"idiom" : "watch",
"filename" : "Graphic Corner.imageset",
"role" : "graphic-corner"
},
{
"idiom" : "watch",
"filename" : "Graphic Large Rectangular.imageset",
"role" : "graphic-large-rectangular"
},
{
"idiom" : "watch",
"filename" : "Modular.imageset",
"role" : "modular"
},
{
"idiom" : "watch",
"filename" : "Utilitarian.imageset",
"role" : "utilitarian"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading

0 comments on commit cc54c25

Please sign in to comment.