Is self captured within a nested function - swift

With closures I usually append [weak self] onto my capture list and then do a null check on self:
func myInstanceMethod()
{
let myClosure =
{
[weak self] (result : Bool) in
if let this = self
{
this.anotherInstanceMethod()
}
}
functionExpectingClosure(myClosure)
}
How do I perform the null check on self if I'm using a nested function in lieu of a closure (or is the check even necessary...or is it even good practice to use a nested function like this) i.e.
func myInstanceMethod()
{
func nestedFunction(result : Bool)
{
anotherInstanceMethod()
}
functionExpectingClosure(nestedFunction)
}

Unfortunately, only Closures have "Capture List" feature like [weak self]. For nested functions, You have to use normal weak or unowned variables.
func myInstanceMethod() {
weak var _self = self
func nestedFunction(result : Bool) {
_self?.anotherInstanceMethod()
}
functionExpectingClosure(nestedFunction)
}

Does not seem to be the case anymore. This is valid in swift 4.1:
class Foo {
var increment = 0
func bar() {
func method1() {
DispatchQueue.main.async(execute: {
method2()
})
}
func method2() {
otherMethod()
increment += 1
}
method1()
}
func otherMethod() {
}
}
The question remains: How is self captured ?

Related

How to append a closure to another closure?

So I have my first closure here:
var instructions: (() -> Void) = {
print("First")
}
instructions() /// prints "First"
Now I have another closure:
let additionalInstructions: (() -> Void) = {
print("Second")
}
additionalInstructions() /// prints "Second"
I want to "append" additionalInstructions to the end of instructions... is this possible? I tried making a new closure containing both of them, like this:
let finalInstructions: (() -> Void) = {
instructions()
additionalInstructions()
}
finalInstructions()
This prints
First
Second
But, when I replace instructions with finalInstructions, I get an EXC_BAD_ACCESS.
instructions = finalInstructions
instructions() /// error here
I think this is because closures are reference types, and some sort of loop happens when instructions contains itself. Is there a way to avoid the error? Making a new closure like finalInstructions, just as a container, also seems kind of clunky.
This is some interesting semantics I didn't expect, but what you're seeing is an infinite recursion, which crashes due to a stack overflow. You can confirm this by running this code in a project, where the debugger will catch the error. You'll see thousands of stackframes of the same closure being called.
It appears that the reference to instructions within your closure is capturing its new, self-referential value, rather than its previous value.
Your approach of using a new variable like finalInstructions is much better. Not only because it avoids this issue, but also because it's much more readable code.
Here's a simpler, more minimal demonstration of the problem:
var closure = { print("initial definition") }
closure() // prints "initial definition"
closure = {
print("second definition")
closure()
}
closure() // prints "second definition" repeatedly, until the stack overflows
FYI, I asked about this on the Swift forums: https://forums.swift.org/t/mutable-closures-can-capture-themselves/43228
It's easy to put them together. It's not easy to take them apart, like C#'s delegates are.
To do that, you'll need to maintain a bunch of junk like below. Better to use Combine, these days.
final class MultiClosureTestCase: XCTestCase {
func test_multiClosure() {
var x = 0
let closures: [EquatableClosure<()>] = [
.init { _ in x += 1 },
.init { _ in x += 2 }
]
let multiClosure = MultiClosure(closures)
multiClosure()
XCTAssertEqual(x, 3)
multiClosure -= closures.first!
multiClosure()
XCTAssertEqual(x, 5)
}
func test_setRemoval() {
let closure: EquatableClosure<()>! = .init { _ in }
let multiClosure = MultiClosure(closure)
XCTAssertNotEqual(multiClosure.closures, [])
XCTAssertNotEqual(closure.multiClosures, [])
multiClosure -= closure
XCTAssertEqual(multiClosure.closures, [])
XCTAssertEqual(closure.multiClosures, [])
}
func test_deallocation() {
var closure: EquatableClosure<()>! = .init { _ in }
var multiClosure: MultiClosure! = .init(closure)
XCTAssertNotEqual(multiClosure.closures, [])
closure = nil
XCTAssertEqual(multiClosure.closures, [])
closure = EquatableClosure { _ in }
multiClosure += closure
XCTAssertNotEqual(closure.multiClosures, [])
multiClosure = nil
XCTAssertEqual(closure.multiClosures, [])
}
}
/// Use this until Swift has unowned-referencing collections.
public final class UnownedReferencer<Reference: AnyObject>: HashableViaID {
public init(_ reference: Reference) {
self.reference = reference
}
public unowned let reference: Reference
}
/// Use this until Swift has weak-referencing collections.
public final class WeakReferencer<Reference: AnyObject>: HashableViaID {
public init(_ reference: Reference) {
self.reference = reference
}
public weak var reference: Reference?
}
/// Remove the first `UnownedReferencer` with this `reference`
public func -= <Reference: Equatable>(
set: inout Set< UnownedReferencer<Reference> >,
reference: Reference
) {
guard let referencer = ( set.first { $0.reference == reference} )
else { return }
set.remove(referencer)
}
/// Remove the first `WeakReferencer` with this `reference`
public func -= <Reference: Equatable>(
set: inout Set< WeakReferencer<Reference> >,
reference: Reference
) {
guard let referencer = ( set.first { $0.reference == reference } )
else { return }
set.remove(referencer)
}
/// A workaround for Swift not providing a way to remove closures
/// from a collection of closures.
///- Note: Designed for one-to-many events, hence no return value.
/// Returning a single value from multiple closures doesn't make sense.
public final class MultiClosure<Input>: Equatable {
public init(_ closures: EquatableClosure<Input>...) {
self += closures
}
public init<Closures: Sequence>(_ closures: Closures)
where Closures.Element == EquatableClosure<Input> {
self += closures
}
var closures: Set<
UnownedReferencer< EquatableClosure<Input> >
> = []
// MARK: deallocation
// We can't find self in `closures` without this.
fileprivate lazy var unownedSelf = UnownedReferencer(self)
// Even though this MultiClosure will be deallocated,
// its corresponding WeakReferencers won't be,
// unless we take this manual action or similar.
deinit {
for closure in closures {
closure.reference.multiClosures.remove(unownedSelf)
}
}
}
public extension MultiClosure {
/// Execute every closure
func callAsFunction(_ input: Input) {
for closure in closures {
closure.reference(input)
}
}
}
public extension MultiClosure where Input == () {
/// Execute every closure
func callAsFunction() {
self(())
}
}
/// A wrapper around a closure, for use with MultiClosures
public final class EquatableClosure<Input>: Equatable {
public init(_ closure: #escaping (Input) -> Void) {
self.closure = closure
}
/// Execute the closure
func callAsFunction(_ input: Input) {
closure(input)
}
private let closure: (Input) -> Void
// MARK: deallocation
var multiClosures: Set<
UnownedReferencer< MultiClosure<Input> >
> = []
// We can't find self in `multiClosures` without this.
fileprivate lazy var unownedSelf = UnownedReferencer(self)
deinit {
for multiClosure in multiClosures {
multiClosure.reference.closures.remove(unownedSelf)
}
}
}
/// Add `closure` to the set of closures that runs
/// when `multiClosure` does
public func += <Input>(
multiClosure: MultiClosure<Input>,
closure: EquatableClosure<Input>
) {
multiClosure.closures.formUnion([closure.unownedSelf])
closure.multiClosures.formUnion([multiClosure.unownedSelf])
}
/// Add `closures` to the set of closures that runs
/// when `multiClosure` does
public func += <
Input,
Closures: Sequence
>(
multiClosure: MultiClosure<Input>,
closures: Closures
)
where Closures.Element == EquatableClosure<Input> {
for closure in closures {
multiClosure += closure
}
}
/// Remove `closure` from the set of closures that runs
/// when `multiClosure` does
public func -= <Input>(
multiClosure: MultiClosure<Input>,
closure: EquatableClosure<Input>
) {
multiClosure.closures.remove(closure.unownedSelf)
closure.multiClosures.remove(multiClosure.unownedSelf)
}
/// An `Identifiable` instance that uses its `id` for equality and hashability.
public protocol HashableViaID: Hashable, Identifiable { }
// MARK: - Equatable
public extension HashableViaID {
static func == (equatable0: Self, equatable1: Self) -> Bool {
equatable0.id == equatable1.id
}
}
// MARK: - Hashable
public extension HashableViaID {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
// MARK: - AnyObject
public extension Equatable where Self: AnyObject {
static func == (class0: Self, class1: Self) -> Bool {
class0 === class1
}
}

Does using a function as a closure retain self?

I'm having trouble tracking down a retain cycle. I think it's to do with the way I subscribe to events. Pseudo code is like this:
override func viewDidLoad() {
func handleEvent() {
self.doSomething()
}
subscribe("eventName", block: handleEvent)
}
deinit {
unsubscribe("eventName")
}
Will this create a retain cycle to self / my ViewController? And if so, how can I get around it? If I was using a closure, I could use [weak self], but since I'm passing a function, is there anyway to use a [weak self] equivalent?
Long story short, your code does retain a reference. (handleEvent->viewDidLoad->self), http://blog.xebia.com/function-references-in-swift-and-retain-cycles/ has some general strategies to avoid the issue. My recommendation would be to create a function reference, rather than declaring a function:
let eventHandler: () -> () = { [weak self] in
self?.doSomething()
}
subscribe("eventName", block: eventHandler)
If you reference a property or method from inside your class it'll create a retain cycle.
class SomeClass {
val a: (block: (() -> ()) -> ()) = ...
func run() {
func b() {
print("Hello, World!")
}
func c() {
self.someMethod()
}
func d() { [weak self]
self?.someMethod()
}
a(block: b) // no retain cycle
a(block: c) // retain cycle
a(block: d) // no retain cycle
}
func someMethod() {
print("Hello, World!")
}
}

Implement a condition which is based on calls from different external IBActions?

I have these two IBActions in WorkoutsController.swift.
#IBAction func startWalkingButton() {
print("Walking start button pressed")
presentControllerWithName("Dashboard", context: sessionContext)
wSM!.startWorkout()
}
#IBAction func startCyclingButton() {
print("Cycling start button pressed")
presentControllerWithName("Dashboard", context: sessionContext)
wSM!.startWorkout()
}
They are calling the startWorkout() function in WorkoutSessionManager.swift
func startWorkout() {
self.healthStore.startWorkoutSession(self.workoutSession)
if ... {
print("startWorkout() called from startWalkingButton")
} else if ... {
print("startWorkout() called from startCyclingButton")
}
}
How do I create a condition to print out different print statements depending on which button function called the method? Should I use an if statement or switch statement?
I know there is already a print statement for the separate IBActions but I want to know if it's possible to do it in the reverse for redundancy.
Simply add one Bool parameter with your method startWorkout
func startWorkout(isFromWalking: Bool) {
if (isFromWalking) {
print("startWorkout() called from startWalkingButton")
}
else {
print("startWorkout() called from startCyclingButton")
}
}
Now call this function from startWalkingButton method with passing true
startWorkout(true)
and from startCyclingButton method with passing false
startWorkout(false)
Edit:
You haven't told that you have multiple option, then best option is to used enum in this case, create one enum like this and use that with the method
enum Workout {
case Walking
case Cycling
//Add case that you have
}
Now change the function like this
func startWorkout(workout: Workout) {
switch(workout) {
case .Walking :
print("Walking")
case .Cycling:
print("Cycling")
}
}
And call the function like this
self.startWorkout(.Walking)
self.startWorkout(.Cycling)
Simply add some sort of 'sender' parameter to your startWorkout() method.
Example:
// Hold a reference to your buttons, connected from IB
#IBOutlet var startWalkingButton: UIButton!
#IBOutlet var startCyclingButton: UIButton!
// here are your .TouchUpInside actions
// UIControl action methods receive the sender of the event as the first parameter (sender)
#IBAction func startWalkingButtonTouched(sender: AnyObject) {
...
startWorkout(sender)
}
#IBAction func startCyclingButtonTouched(sender: AnyObject) {
...
startWorkout(sender)
}
func startWorkout(sender: AnyObject) {
self.healthStore.startWorkoutSession(self.workoutSession)
switch sender {
case startWalkingButton:
print("startWorkout() called from startWalkingButton")
break
case startCyclingButton:
print("startWorkout() called from startCyclingButton")
break
default: ()
}
}
Hope this helps.
I feel you should probably use a block here. startWorkout method should accept an optional block. This approach avoids passing arguments and also avoids having if and case statements.
class Walking {
let workout = Workout()
func startWalkingButton() {
print("startWalkingButton")
workout.startWorkout() {
print("Walking Over")
}
}
}
class Cycling {
let workout = Workout()
func startCyclingButton() {
print("startCyclingButton")
workout.startWorkout() {
print("Cycling Over")
}
}
}
class Workout {
func startWorkout( afterWorkout: () -> Void ){
print("startWorkout")
afterWorkout()
}
}
let w = Walking()
w.startWalkingButton()

Unowned self in a closure in a closure

If I have a closure in another closure is it enough to use unowned/weak once in the outer closure to avoid retain cycles?
Example:
foo.aClosure({[unowned self] (allowed: Bool) in
if allowed {
self.doStuff()
self.something.anotherClosure({ (s:String) -> (Void) in
self.doSomethingElse(s)
})
}
})
Only declaring weak or unowned self in the capture list of the outer closure is enough to avoid retain cycles if you don't create a strong reference to self within the outer closure (e.g. by doing: guard let strongSelf = self else { return }).
If you do create a strong reference within the closure, you must add a capture list to the inner closure to ensure that it captures your strong reference to self weakly.
Here are some examples:
import Foundation
import PlaygroundSupport
class SomeObject {
typealias OptionalOuterClosure = ((Int) -> Void)?
typealias InnerClosure = () -> Void
var outerClosure: OptionalOuterClosure
func setup() {
// Here are several examples of the outer closure that you can easily switch out below
// All of these outer closures contain inner closures that need references to self
// optionalChecks
// - has a capture list in the outer closure
// - uses the safe navigation operator (?) to ensure that self isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let optionalChecks: OptionalOuterClosure = { [weak self] callNumber in
print("outerClosure \(callNumber)")
self?.delayCaller { [weak self] in
print("innerClosure \(callNumber)")
self?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - has a capture list in the inner closure
// - uses the safe navigation operator (?) to ensure strongSelf isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let copiedSelfWithInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller { [weak strongSelf] in
print("innerClosure \(callNumber)")
strongSelf?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithoutInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - does NOT have a capture list in the inner closure and does NOT use safe navigation operator
// this closure DOES retain self, so you should see the doSomething #2 call below
let copiedSelfWithoutInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller {
print("innerClosure \(callNumber)")
strongSelf.doSomething(callNumber: callNumber)
}
}
// retainingOuterClosure
// - does NOT have any capture lists
// this closure DOES retain self, so you should see the doSomething #2 call below
let retainingOuterClosure: OptionalOuterClosure = { callNumber in
print("outerClosure \(callNumber)")
self.delayCaller {
print("innerClosure \(callNumber)")
self.doSomething(callNumber: callNumber)
}
}
// Modify which outerClosure you would like to test here
outerClosure = copiedSelfWithInnerCaptureList
}
func doSomething(callNumber: Int) {
print("doSomething \(callNumber)")
}
func delayCaller(closure: #escaping InnerClosure) {
delay(seconds: 1, closure: closure)
}
deinit {
print("deinit")
}
}
// Handy delay method copied from: http://alisoftware.github.io/swift/closures/2016/07/25/closure-capture-1/
func delay(seconds: Int, closure: #escaping () -> Void) {
let time = DispatchTime.now() + .seconds(seconds)
DispatchQueue.main.asyncAfter(deadline: time) {
print("🕑")
closure()
}
}
var someObject: SomeObject? = SomeObject()
someObject?.setup()
// Keep a reference to the outer closure so we can later test if it retained someObject
let copiedOuterClosure = someObject!.outerClosure!
// Call the outer closure once just to make sure it works
copiedOuterClosure(1)
// Wait a second before we destroy someObject to give the first call a chance to work
delay(seconds: 1) {
// Run the outerClosure again to check if we retained someObject
copiedOuterClosure(2)
// Get rid of our reference to someObject before the inner closure runs
print("de-referencing someObject")
someObject = nil
}
// Keep the main run loop going so our async task can complete (need this due to how playgrounds work)
PlaygroundPage.current.needsIndefiniteExecution = true
Yes, however I would use weak over unowned because self.doStuff() with throw an exception if nil while if you use weak and its self?.doStuff() there won't be an exception thrown and it just won't execute.
You can test this in the playground with the following code:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {[weak self] in
self?.closureObject.saveClosure() {
self?.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()
and compare it with:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {
self.closureObject.saveClosure() {
self.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()

How to use #autoclosure parameter in async block in Swift?

I would like to call an #autoclosure parameter inside dispatch_async block.
func myFunc(#autoclosure condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
I get the following error.
Closure use of #noescape parameter may allow it to escape.
Is it possible to call #autoclosure parameter asynchronously?
Tested in Xcode 6.4 (6E23).
Yes, so long as you declare them #autoclosure(escaping):
Declarations with the autoclosure attribute imply noescape as well, except when passed the optional attribute escaping.
So this should do it:
func myFunc(#autoclosure(escaping) condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
Updating the answer from Airspeed Velocity, you can pass weak self directly into escaping autoclosure.
var boolValue: Bool = true
func myFunc(condition: #autoclosure #escaping () -> Bool?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let condition = condition() {
print("Condition is \(condition ? "true" : "false")")
}
}
}
func main() {
weak var weakSelf = self
myFunc(condition: weakSelf?.boolValue)
}