Accessing Self from Instance Properties Which Are Closures

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

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.

Using self in instance closure variables

The problem is that you are using self before the initialization of an instance of A.

So there is not self yet.

If you move the creation fo your closure inside the init it will work

class A {
var value:Int = 3
var someFn : () -> () = { }

init() {
someFn = { print(self) }
}
}

A().someFn() // A

I needed to populate someFn with an empty value during the declaration, in order to be able to use self inside the init. Infact self cannot be used (inside the init) before all the properties without a default value have been initialized.

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
}
}

Accessing a closure property of an instance in RxSwift

Point 1 - Am I accessing a string property of a instance, right ?

Strictly speaking yes, but the instance will be created just for this access and then immediately destroyed. This is rather silly to do IMO.

Point 2ab - I think, I am accessing an instance after calling a closure,Actually what am I doing here?

Point 2a creates a Client value (not the same one used in Point 1, but it will have the same value.)

Point 2b calls setable() on the value. The value object will be destroyed at the end of the viewDidLoad function.

Point 3 - Getting error, Cannot convert value of type '()' to closure result type 'Observable<ViewController.Client.DelegateEvent>'

The Client.live computed property is a global, you are trying to access an instance variable of a particular ViewController from inside that global, but there is no way to define which instance should be accessed.

So How can I fix this in a meaningful way ?

This question I cannot answer because I don't know what "fixed" means in this context...

How does self property refer to Instance object when using escaping closure ? (Swift 3)

Below in comments, you ask:

Inside of instance class, self.x equals instance property x. Outside of instance class when we call closure, is self.x still instance property x?

Yes. The self inside the closure refers to the object that created the closure, not to the object that ends up calling the closure (possibly much later). So if object A passes a closure that refers to self.x to another object, B, when B calls that closure, the self.x still refer's to A's x property, not B's.


In answer to the original question, yes, you can refer to whatever instance you want. It doesn't have to be self.

The abstract way you phrase the question, though, makes me a bit suspicious. This question of "can I update any other instance" introduces some faint hints of code smell. The advantage of the closure pattern is that you're letting the object initiating some action to supply a closure that dictates what should happen in the first object when this other object finishes some task, keeping everything nicely, loosely coupled.

But when you start updating other unrelated objects, things start to feel a bit more tightly coupled, again. It all comes down to what this instance is and what relationship it bears to the self that created the closure.

But, in short, yes, you can update properties or call methods on items other than self, but we'd need to better understand what those various objects were to comment on whether it's a good idea in this case or not.

Why cannot we use instance members in a closure definition?

The problem is that I'm trying to initialize a variable using an instance's variable while the instance itself has not been created yet.
So the problem is not related to closure nature. The same problem does exist in the following code where I'm trying to initialize nickname with name instance property.

class Person {

var name: String = "Amirreza"
var nickname: String = name
}

As compiler says, "property initializers run before 'self' is available", so in the above code the self.name is not available to initialize nickname and this is the source of the problem.

Here the solution is to use "lazy" keyword before nickname definition. The lazy keywork defers the initialization of the nickname property to when it is accessed for the first time. Means when we are sure that the instance has been created and name property is available.

To read more about lazy refer to https://docs.swift.org/swift-book/LanguageGuide/Properties.html

So the correct form of above code is:

class Person {
    
var name: String = "Amirreza"
lazy var nickname: String = name
}

And the correct form of the my main question code would be:

class Person {

var name: String = "Amirreza"

lazy var myClosure = { [weak self] (family: String) -> Void in
print(self!.name + " " + family)
}

func myFunc(family: String) -> Void {
print(self.name + " " + family)
}
}

Just note that we have to refer to properties inside closures with self explicitly like self.name. Meanwhile to avoid strong reference cycles we need to define a capture list in closure definition which in my example is [weak self].
By defining self as 'weak', self turns to an optional variable, so we have to unwrap it somehow which makes me to write self!.name in the closure.

Swift must use lazy for stored property when use self in closure?

When you are declaring label with lazy initial value is not calculated until the first time it is used.
so the probably the Instantiates views was completed.

But the concept of using let in Swift, variables which are let have to be initialized before you can use self.

Using lazy var means that the compiler can verify that the value assigned to label won't be accessed before self is a valid object, because it won't be possible to call label until all other members of the class have been initialized.

Swift closures in autorelase pool accessing methods without self

Calling an instance method or referencing an instance property in a closure requires explicit self if that closure is passed to a function taking an @escaping parameter. That makes it explicit that the self is possibly captured beyond the duration of the function call.

If the function parameter is not @escaping then no explicit self is required.

Here is a self-contained example:

func funcTakingNonescapingClosure(_ closure: () -> Void) { }
func funcTakingEscapingClosure(_ closure: @escaping () -> Void) { }

class Foo {

func anyMethod() { }
var myVariable = ""

func test() {
funcTakingNonescapingClosure {
anyMethod() // No error
print(myVariable) // No error
}

funcTakingEscapingClosure {
anyMethod()
// Call to method 'anyMethod' in closure requires explicit 'self.'
// to make capture semantics explicit
print(myVariable)
// Reference to property 'myVariable' in closure requires explicit
// 'self.' to make capture semantics explicit
}
}
}

In your example, Dispatch.main.async takes an escaping closure, but autorelease not.



Related Topics



Leave a reply



Submit