As the apple described in swift documentation that closures are reference type. So every time when I assign a closure to another reference variable then only reference will be copied. I tried below code and here I found a strange behaviour of swift closures. Here I created an object of MyClass and assigned It to obj. And this object is captured by closure which is referred by cls1, cls2 and cls3. When I call this method, then I get retain count of MyClass object is 5. But if closures are reference type only retain count of closure object should increase not the MyClass object but here retain count of MyClass object is increased.
class ABC{
class MyClass{}
static func get(){
let obj: MyClass = .init()
let cls1: (()->Void) = {
print(obj)
}
let cls2 = cls1
let cls3 = cls1
let count = CFGetRetainCount(obj)
print(count)
}
}
Firstly, you can't reliably use retain counts to track object ownership. There are too many complicating factors that are outside of your awareness or control.
Second, you forget that closures capture objects from their enclosing scope.
Related
I have a class
class SomeClass {
lazy var property1: Int = {
return 1
}()
lazy var property2: Int = {
return self.property1
}()
deinit {
print("SomeClass will be destroyed")
}
}
If I have the following in playground:
var a: SomeClass? = SomeClass()
print(a?.property1)
print(a?.property2)
a = nil
then the variable a will be deinitialized as SomeClass will be destroyed message will appear.
However if I comment out the accesses to the properties like:
var a: SomeClass? = SomeClass()
//print(a?.property1)
//print(a?.property2)
a = nil
I still got the message SomeClass will be destroyed.
I would expect that there is a reference cycle as the closure of property2 is never called and it references self through self.property1.
Is there something special with lazy variables or just my assumption is wrong that property2 hold a reference to self?
The reason that there isn't is a reference cycle is because the closure isn't stored on a. If a stored the closure and the closure contained self then there would be a reference cycle.
For lazy properties, everything after the = isn't called until the first time you access property2. When you do, the closure is created, called, freed from memory, and then the value is returned to you. This is why you can get rid of the self. in the closure since it will never capture self.
I experimented it with the following code in Xcode Playground:
class X {
var a = 3
init(a: Int) {
self.a = a
}
deinit {
print("\(self.a) is deallocated.")
}
func returnX() -> Int {
return self.a
}
lazy var anotherReturnX: () -> Int = {
return self.a
}
}
var e: X? = X(a: 6)
print(e!.returnX())
e = nil // prints "6 is deallocated."
var f: X? = X(a: 7)
print(f!.anotherReturnX())
f = nil // prints nothing
From the above code, I can see that no reference is captured in the function returnX(), thus e is deallocated once I set e to nil. However, a reference is captured in the closure anotherReturnX(), thus f is not deallocated. Apparently, this implies that a closure captures references while a function does not.
Additionally, when I first type out the code, I didn't include lazy keyword before the closure declaration, as I thought it would be unnecessary to do so. However it triggers a compile-time error. I infer that since the closure can only be accessed after instantiation, it must access to the instantiated self. But since what I am declaring here is effectively an "anonymous function", why would the closure access to self during instantiation anyway?
After putting in some thoughts, I found more contradictions. For instance, I understand that a reference is captured when a closure is called. However, during initialisation of X, I am simply assigning a closure to a variable without calling it, same as the declaration of other instance properties. Thus the closure should not do anything during initialisation, and compiling the code without keyword lazy should be fine. But compilation fails. I am not sure what goes wrong in my understanding.
I have read some related articles, such as strong/weak reference, retain cycle, lazy stored property. However, many explain "what happens" and do not say much about "why".
So other than the question raised in the title, I would also like to clarify, what makes function and closure different from each other such that the above situation happens?
Update:
The fact that closure captures reference while function does not is further "enforced" on me, since, according to the Xcode Compiler, I can rewrite return self.a as return a in returnX(), but I cannot do so in anotherReturnX. As such, I guess I would have to accept that, although function and closure are similar because each of them is "a bundle of functionalities", function is different from closure that it does not capture references. If I were to go deeper in the reason behind this, it would probably involve the design of Swift itself?
However, I still cannot understand why lazy keyword is required for closure declaration.
lazy var anotherReturnX: () -> Int = {
return self.a
}
The self here is a strong self.
When an object references another object strongly, ARC cant deallocate so a retain cycle is created. The reference should be weak to avoid a retain cycle by creating weak self inside the block.
lazy var anotherReturnX: () -> Int = { [weak self] in
return self?.a
}
returnX is a method of the class. Methods don't capture variables. self is an implicit local variable in methods, that is implicitly passed to the method when the method is called.
anotherReturnX is a property that is assigned a closure lazily. That closure captures outside variables that are used within it, including self. That capturing creates a strong reference from the closure to the X instance, which, combined with the strong reference from the X instance to the closure, creates a retain cycle.
I am overloading (or maybe implementing in this case) the += operator for a class (not a struct!). The operation modifies the state of the left-hand-side instance. I noticed that I can declare the left-hand-side element with let without any errors (and since it is an instance of a class, it's internal state changes with the operation). This of course is undesired, and should result in a compile-time-error. Is there a way to declare the overloaded operator as mutating to the left-hand-side element?
class MyClass {
static func +=(lhs: MyClass, rhs: MyClass) {
lhs.fu(rhs) // fu() changes internal state of lhs
}
}
let a = MyClass()
let b = MyClass()
a += b // this is legal but shouldn't be, since instance 'a' will
// have a different internal state after the concatenation
The let constant in this case is the reference a to the MyClass object that it points to. It prevents you from being able to do this:
let a = MyClass()
a = MyClass() //redefinition not allowed
It does not guarantee anything about the constancy of the members of that object however. Classes/objects exist to model constantly changing data, marking methods as mutating would be a bit tedious, because ultimately that's what they're supposed to do, in general.
You should be using structs in cases where you want controlled mutation.
Question: For the case of ()-return closures, is there any way to access a variable that ARC lets live only due to a single strong reference from a closure closing over it? In the example below: accessing bb in the closure.
Below follows an example to show what I mean.
In the Language Reference - Expressions, it reads
Capture Lists
By default, a closure expression captures constants and variables from
its surrounding scope with strong references to those values. You can
use a capture list to explicitly control how values are captured in a
closure.
...
Consider the following example, using a weak and a strong capture of two class type instances
class MyClass {
var myInt : Int = 0
func printMyInt() {
print(myInt)
}
}
func getClosure(a: MyClass, _ b: MyClass) -> (() -> ()) {
return { [weak aa = a, bb = b] in
aa?.printMyInt() ?? print("Lost reference")
bb.printMyInt()
}
}
func foo() -> (() -> ()) {
let a = MyClass()
let b = MyClass()
let closure = getClosure(a, b)
closure() // 0, 0
a.myInt = 1
b.myInt = 2
closure() // 1, 2
return closure
}
If foo() is called, then at the return of closure, MyClass instances a and b are out of scope. In the closure itself, aa keeps a weak reference to a, so a (memory) will be "destroyed" by ARC, and aa will become nil.
However, since the closure closes over b with a strong reference, ARC will retain the memory for b until the closure itself goes out of scope.
let closure = foo()
closure() // Lost reference, 2 <-- OK, expected
/* 'b' (not the reference, but the object in memory) still lives, but
cant be explicitly accessed? */
Hence my question: how to access, in this case, bb within the closure.
What I've tried
I've tried without success using Mirror:
var bar = Mirror(reflecting: closure)
print(bar.children.count) // 0
Also, I know we can "access" bb in the example above by adding a return type (MyClass) to the closure, but I'm wondering if we can actually access it without such a workaround (hence the ()-return specific closure in the question above).
Where I've looked prior to posting this question
I've been searching around SO for a possible existing threads that asks and answers this question, but the closest one I found,
Getting data out of a closure in swift,
don't really answer my question here. (Or perhaps the workarounds in the answers to that question does, and the answer to my question above is "you can't"?)
I need to do this, but I'm having all sorts of problems:
class MyClass {
}
class SomeClass {
func callMethod(object) {
let theType = object.type // I need the same result as MyClass.self
let newInstance = object() // I need the same result as MyClass()
}
}
let someClass = SomeClass()
let object = MyClass
someClass.callMethod(object)
How do you actually do this?
I want to be able to specify one type (e.g. MyClass). That can be either the type name or an instance of it and then get the two values shown in the code comments.
It would be a lot easier if I passed in two separate variables, but I'm sure I can do it in one.
You can use the .dynamicType property of an instance to get its type. Then you can call a required init method on it (or any other class/static method).
let theType = object.dynamicType
let newInstance = theType.init()
Note: This code compiles, depending on the type of object. If the type has a required init() method, it is safe to call on the dynamicType. However, it also appears that if the type is NSObject the compiler will let you call init() on the dynamicType even though a subclass may or may not have an init() method given how Swift initializer inheritance works. Arguably this is a bug in the compiler, so use this code with caution if you choose to do so. Thanks to #nhgrif for the discussion on the topic.