Accessing "Self" in Initializing Closure

Accessing self in initializing closure

This quoted example of the Migration Guide is misleading because it's related to a global variable.

The closure of a instance let constant is called (once) immediately when the class is initialized. That's the reason why it cannot use other variables declared on the same level.

What you can do is to initialize initialize (the variable name is not the best one ;-) ) lazily. The closure is also called only once but – as the guide describes – only the first time (when) it is used.

class SomeClass {
let other = SomeOtherClass()

lazy var initialize : () = {
let test = self.other
test.doSomething()
}()

func doSomething() {
// initialize will only be called once
_ = initialize
}
}

Swift - `self` in variable initialization closure

It works in NSObject subclasses because NSObject (or rather NSObjectProtocol) declares method self. That method is also available on metatypes (which are also NSObject instances) and therefore you can call it in static context.

The fact that it actually works on UIButton is probably a quirk of the compiler and the fact that UIButton accepts Any? as target.

Don't use it, it's not how it's intended to work.

See the bug SR-4559

Why can i use self when I initialize property with a closure?

The target in your code is probably nil, the reason that this might work is that:

...If you specify nil, UIKit searches the responder chain for an
object that responds to the specified action message and delivers the
message to that object

(from the documentation of the method)

You can verify that the target is actually nil by setting a breakpoint during or after your controller's init and by inspecting the _targetActions array (in the debugger's variables view) of the button (you can see that target's address is 0x0).

In your example, loginRegisterButton is set during the controller's initialization which means that there is no self yet (in other words you cannot guarantee that all instance variables are initialized at this point). The way lazy solves this is that the actual assignment is deferred on first access.

self captured by a closure before all members were initialized - but I did initialize them

While I'm not entirely sure why Swift doesn't allow this (something to do with capturing self to create the closure before the actual call to super.init is made), I do at least know of a workaround for it. Capture a weak local variable instead, and after the call to super.init set that local variable to self:

class MyDataSource: UITableViewDiffableDataSource<String,String> {
var string : String?
init(string:String?) {
self.string = string
weak var selfWorkaround: MyDataSource?
super.init(tableView: UITableView()) { (_, _, _) -> UITableViewCell? in
print(selfWorkaround?.string)
return nil
}

selfWorkaround = self
}
}

The only problem with this, though, is that if the closure is executed during the call to super.init, then selfWorkaround would be nil inside the closure and you may get unexpected results. (In this case, though, I don't think it is - so you should be safe to do this.)

Edit: The reason we make the local variable weak is to prevent the self object from being leaked.

Accessing self from instance properties which are closures

This looks interesting, so I dig little deeper. Found that, you can access the class instance variables within the closure like self.instanceVariable. Then the closure will capture the self within it. So now the self refers to the class instance itself. Your closure should be a lazy property.

A lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist.

You are missing @lazy so that self is unknown to the closure thats why it is printing it as (Function) my guess.

class TableViewController: UIViewController {
var name = "anil"
// Since swift 2.0 came out @lazy is replaced by lazy
lazy var c1: () -> () = {
println(self)
println(self.name)

}

var c2: () -> () {
get {
return { println(self) }
}
}

var c3: () -> () {
return { println(self) }
}

override func viewDidLoad() {
super.viewDidLoad()
c1()
c2()
c3()
}
}

Output

<_TtC12TableViewApp19TableViewController: 0x10d54e000>
anil

<_TtC12TableViewApp19TableViewController: 0x10d54e000>
<_TtC12TableViewApp19TableViewController: 0x10d54e000>


Update

Assigning closure to a class instance variable results strong reference cycle. You should avoid this. Swift uses Capture list for that

If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles. For more information, see Strong Reference Cycles for Closures.

So the correct usage of closure could be

@lazy  var c1: () -> () = {
[unowned self] in
println(self)
println(self.name)

}

Reference: Swift programming guide

Edit
@lazy has been changed to lazy

SwiftUI view, initialize closure with struct member says 'self' used before all stored properties are initialized

Try the following (viewModel is reference type, so you can manipulate with it as temporary variable)

init(currentSelectedTab: Tab) {
let vm = TabScreenViewModel(
input: TabScreenInput(
onStart: PassthroughSubject(),
onTabClick: PassthroughSubject()
)
)
self.viewModel = vm
onTabItemTap = {
[vm](tab: Tab) -> Void in
vm.input.onTabClick.send(tab)
}
}

how to deal with 'Self captured by closure before being initialized'

The problem with your code is that you have declared the variable as a constant, and swift should always have constants initialized within the init method. But, here you have dependent kind of requirement, readyHandler property is a constant which has to be initialized in order to create object but then, you are using self inside it which is not initialized, you can see a cyclic requirement.

You can directly get rid of this, if you use optional or implicitly unwrapped optional in which case, swift need not have initial value at the phase of instantiation.

class A {
class ReadyHandler { // fires off the callback when needed
let callback: ()->Void
init(callback: @escaping ()->Void) {
self.callback = callback
}
}
var readyHandler: ReadyHandler!
var ready = false

init() {
readyHandler = ReadyHandler(callback: {self.ready = true})
}
}

You can create a also use lazy property for your readyHandler which gets initialized first time it is used.

class A {
class ReadyHandler {

let callback: ()->Void

init(callback: @escaping ()->Void) {
self.callback = callback
}

}

var ready = false

lazy var readyHandler: ReadyHandler = {
return ReadyHandler(callback: { [unowned self] in
self.ready = true
})
}()
}

Property Initialization with closures

This is not a retain cycle because self is not what you think it is :)

Properties with initial value are "executed" even before any initializer runs, and for those properties self points to a higher order function of this type:

(TestClass) -> () -> TestClass

So you don't really access the instance, but rather you access a static-like method that does the initialization of all properties that have a default value. This is why you don't have a retain cycle.

addTarget accepts an Any? value for it's first argument, so this violates no type rules so the compiler doesn't complain that you don't pass a NSObject instance there.

Checking the later behaviour - e.g. what happens if the button is added to the UI hierarchy and is tapped, reveals something interesting: the runtime sees that you passed a non-object as a target and sets null values for target and action:

Sample Image

Closure - Timing of deinit self object

First of all you need to know definition of lazy variable.

According to apple docs, A lazy stored property is a property whose initial value isn’t calculated until the first time it’s used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

Means that when the class is initial, they don't know they have variable lazy inside which is incrementNumber.

That the reason when you call

do {
let increment = Increment().incrementNumber(3)
}

Increment don't recognized incrementNumber in the class when you access directly. So Swift only see that you do nothing with Increment class in the rest of code then it automatically deinit the unused class.

Update: As Mr. DuncanC's mentioned, because of deinit class first so the compiler create an instance of Increment as AutoReleased, and keep it in memory in order to evaluate the second part of the expression

At the second, you call

let increment = Increment()
increment.incrementNumber(3)

Means that you make a class at first then you make lazy variable ( Swift sees that you do something with that class in second line so it waits until everything in class is called). Then in the rest of code class Increment is unused then it automatically deinit. That's the reason you see lazy is call before deinit.

For more further knowledge, you can do like making a not lazy function to see the difference.

class Increment {
var number = 0

init(){
print(#function)
}

deinit {
print(#function)
}

public func increaseNumber(_ value: Int) {
self.number += value
print(#function)
}
}

do {
let increment = Increment().increaseNumber(3)
}

//init()
//increaseNumber(_:)
//deinit

As you can see that increaseNumber is called before deinit because the class know that it owns func increaseNumber so it was called. Then in the rest of code class Increment is unused then it automatically deinit.

Referencing self in super.init

Short answer:

You cannot capture and use self as a value before super.init returns. In your case, you are trying to "pass" self to super.init as an argument.

As per why the second part works, simply because without using self in it, it does not capture self, thus it does not use self as a value.

If you don't want to use self in the closure, then you don't need to worry about strong/weak reference there, because there is no reference to self there at all (since it was not captured). No danger of retain cycle.


Short sidenote about "using self as a value" - you can use self on the left-hand side of an assignment to refer to properties of the self when initializing them:

let myProperty: String

init(with myProperty: String) {
// this usage of self is allowed
self.myProperty = myProperty
super.init(nibName: nil, bundle: nil)
}

Longer answer with references and stuff:

As per documentation:

Safety check 4

An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

First phase of initialization is ended by calling super.init, when the

From the same documentation:

Phase 1

A designated or convenience initializer is called on a class.

Memory for a new instance of that class is allocated. The memory is not yet initialized.

A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.

The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.

This continues up the class inheritance chain until the top of the chain is reached.

Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

So only after calling super.init you are allowed to use self as value:

Phase 2

Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.

Finally, any convenience initializers in the chain have the option to customize the instance and to work with self.

Now I am not surprised at all that when you try to use self as a value in a capture list of the closure, that it crashes. I am more surprised that the compiler does allow you to do it - now I guess it's an edge case for which error handling wasn't implemented.

In the second case:

Action(title: "Yes", {
//Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})

You don't really capture self, that's why it is allowed and it works. But you don't have access to self there. Try to add there some code that uses self and the compiler will complain:

Sample Image

So in the end, if you want to use self in the closure, you will have to find a way how to first call super.init and only after that add self capturing closures to the properties.



Related Topics



Leave a reply



Submit