document `weak(self, in:)` and add more tests

This commit is contained in:
Yonat Sharon 2020-02-03 13:10:34 +02:00
parent b2ca11115d
commit 560220369a
3 changed files with 29 additions and 8 deletions

View File

@ -171,6 +171,12 @@ Unwrap collection, shorthand for `compactMap { $0 }`:
let nonOptionals = optionalsCollection.compact
```
Avoid retain cycles when passing a member function as an @escaping closure:
```swift
var closure = weak(self, in: MyClass.someFunction)
```
### TimeInterval
Standard time intervals:

View File

@ -61,9 +61,11 @@ extension IndexSet: DefaultConstructible {}
extension CharacterSet: DefaultConstructible {}
extension NSObject: DefaultConstructible {}
/// Pass a member function as an @escaping closure without retaining its object.
// based on https://sveinhal.github.io/2016/03/16/retain-cycles-function-references/
/// Sweeter: Pass a member function as an @escaping closure without retaining its object.
///
/// Example: `var fooRef = weak(self, in: MyClass.foo)`
/// Example: `var closure = weak(self, in: MyClass.someFunction)`
public func weak<T: AnyObject>(_ instance: T, in classFunction: @escaping (T) -> () -> Void) -> () -> Void {
return { [weak instance] in
guard let instance = instance else { return }
@ -71,9 +73,9 @@ public func weak<T: AnyObject>(_ instance: T, in classFunction: @escaping (T) ->
}
}
/// Pass a member function as an @escaping closure without retaining its object.
/// Sweeter: Pass a member function as an @escaping closure without retaining its object.
///
/// Example: `var fooRef = weak(self, in: MyClass.foo)`
/// Example: `var closure = weak(self, in: MyClass.someFunction)`
public func weak<T: AnyObject, U>(_ instance: T, in classFunction: @escaping (T) -> (U) -> Void) -> (U) -> Void {
return { [weak instance] arguments in
guard let instance = instance else { return }
@ -81,9 +83,9 @@ public func weak<T: AnyObject, U>(_ instance: T, in classFunction: @escaping (T)
}
}
/// Pass a member function as an @escaping closure without retaining its object.
/// Sweeter: Pass a member function as an @escaping closure without retaining its object.
///
/// Example: `var fooRef = weak(self, in: MyClass.foo)`
/// Example: `var closure = weak(self, in: MyClass.someFunction)`
public func weak<T: AnyObject, V: DefaultConstructible>(_ instance: T, in classFunction: @escaping (T) -> () -> V) -> () -> V {
return { [weak instance] in
guard let instance = instance else { return V() }
@ -91,9 +93,9 @@ public func weak<T: AnyObject, V: DefaultConstructible>(_ instance: T, in classF
}
}
/// Pass a member function as an @escaping closure without retaining its object.
/// Sweeter: Pass a member function as an @escaping closure without retaining its object.
///
/// Example: `var fooRef = weak(self, in: MyClass.foo)`
/// Example: `var closure = weak(self, in: MyClass.someFunction)`
public func weak<T: AnyObject, U, V: DefaultConstructible>(_ instance: T, in classFunction: @escaping (T) -> (U) -> V) -> (U) -> V {
return { [weak instance] arguments in
guard let instance = instance else { return V() }

View File

@ -28,11 +28,22 @@ class SwiftTests: XCTestCase {
func testWeakSelfInMemberFunction() {
class Foo: DefaultConstructible {
static var refCount = 0
func foo() {}
func fooWithParam(n: Int) {}
func fooWithReturn() -> String { "" }
func fooWithParamAndReturn(array: [Character]) -> Double { 0 }
lazy var fooRef = weak(self, in: Foo.foo)
lazy var fooWithParamRef = weak(self, in: Foo.fooWithParam)
lazy var fooWithReturnRef = weak(self, in: Foo.fooWithReturn)
lazy var fooWithParamAndReturnRef = weak(self, in: Foo.fooWithParamAndReturn)
required init() {
fooRef()
fooWithParamRef(7)
_ = fooWithReturn()
_ = fooWithParamAndReturnRef([])
Foo.refCount += 1
}
@ -41,6 +52,8 @@ class SwiftTests: XCTestCase {
}
}
XCTAssertEqual(Foo.refCount, 0)
for _ in 0 ..< 3 {
let aFoo = Foo()
aFoo.foo()