Is self captured within a nested function
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)
}
Am I capturing self in this nested function? The compiler does not fire a warning
When you assign a closure to tableViewHandler.didSelectRow
, you assign to it and retain whatever that closure captures.
self
is retaining tableViewHandler
.
Therefore, the danger is that you will refer to self
within the closure. If you do, that's a retain cycle.
And this might not be due to referring to self
explicitly. Any mention of a property or method of self
is an implicit reference to self
.
Okay, so with that out of the way, let's examine the closure.
You do not mention self
implicitly or explicitly in the body of the closure. However, you do call a local method, categorySelected
. Therefore, you capture this method.
And categorySelected
does mention self
. Therefore, it captures self
(because every function is a closure).
Thus there is a potential retain cycle and you should continue to say unowned self
to prevent a retain cycle.
(I presume that the compiler can't help you here by warning of the retain cycle; there's too much complexity. It's a problem you just have to solve by human reason.)
Why does a nested self-capturing function interfere with isKnownUniquelyReferenced(_:)?
Swift 3.1 Update
As of Swift 3.1, available with Xcode 8.3 beta, this has been fixed. self
is no longer boxed at all in the method, therefore isKnownUniquelyReferenced(_:)
returns true
as expected.
Pre Swift 3.1
I consider this a bug, and have filed a bug report (SR-3530). I was however interested by the cause of this problem, so did some digging – and this is what I found.
Taking a look at the canonical SIL generated for the bar()
method (for an -Onone build) reveals that Swift is heap allocating a box (alloc_box
) for self
at the very beginning of the method – so that it can be captured by nestedFunc()
.
// Bar.bar() -> ()
sil hidden @main.Bar.bar () -> () : $@convention(method) (@inout Bar) -> () {
// %0 // users: %10, %3
bb0(%0 : $*Bar):
// create new heap-allocated box, and store self in it.
// this is where the problem stems from – there are now two copies of the Bar instance, thus isKnownUniquelyReferenced will return false.
%1 = alloc_box $Bar, var, name "self", argno 1, loc "main.swift":15:26, scope 9 // users: %11, %9, %7, %2
%2 = project_box %1 : $@box Bar, loc "main.swift":15:26, scope 9 // users: %10, %5, %3
copy_addr %0 to [initialization] %2 : $*Bar, scope 9 // id: %3
// call isKnownUniquelyReferenced (I removed the print() function call as it generates a bunch of unrelated SIL).
// function_ref isKnownUniquelyReferenced<A where ...> (inout A) -> Bool
%4 = function_ref @Swift.isKnownUniquelyReferenced <A where A: Swift.AnyObject> (inout A) -> Swift.Bool : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@inout τ_0_0) -> Bool, loc "main.swift":17:9, scope 10 // user: %6
%5 = struct_element_addr %2 : $*Bar, #Bar.foo, loc "main.swift":17:35, scope 10 // user: %6
%6 = apply %4<Foo>(%5) : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@inout τ_0_0) -> Bool, loc "main.swift":17:39, scope 10
// retain the heap-allocated box containing self, in preparation for applying nestedFunc() with it.
// (as it's passed as an @owned parameter).
strong_retain %1 : $@box Bar, loc "main.swift":27:9, scope 10 // id: %7
// call the nested function with the box as the argument.
// function_ref Bar.(bar() -> ()).(nestedFunc #1)() -> ()
%8 = function_ref @main.Bar.(bar () -> ()).(nestedFunc #1) () -> () : $@convention(thin) (@owned @box Bar) -> (), loc "main.swift":27:9, scope 10 // user: %9
%9 = apply %8(%1) : $@convention(thin) (@owned @box Bar) -> (), loc "main.swift":27:20, scope 10
// once called, copy the contents of the box back to the address of the Bar instance that was passed into the method, and release the box.
copy_addr %2 to %0 : $*Bar, scope 10 // id: %10
strong_release %1 : $@box Bar, loc "main.swift":29:5, scope 10 // id: %11
// so cute.
%12 = tuple (), loc "main.swift":29:5, scope 10 // user: %13
return %12 : $(), loc "main.swift":29:5, scope 10 // id: %13
}
(Full SIL here)
Because of this boxing, there are now two copies of the Bar
instance in the bar()
method, therefore meaning that isKnownUniquelyReferenced(_:)
will return false, as there are two references to the Foo
instance.
From what I can tell, the boxing of self
at the beginning of the method seems to be a consequence of the optimisation of the mutating
method from copy-in copy-out (self
gets boxed at the start of the method call, mutations are then applied to that box, then written back to the callee at the end of the method) to pass-by-reference (this optimisation occurs between the raw SIL and canonical SIL).
The same box that was used in order to create a copy of self
to mutate within the method is now used in order to capture self
to call the nested function with. I see no reason why the box for the capture shouldn’t be created just before the call to nestedFunc()
, as that’s the logical place to capture self
(rather than at the beginning of the method).
Although, in any case, the creation of a box is completely redundant in the first place, as nestedFunc()
doesn’t and cannot escape. Attempting to return nestedFunc()
from the method yields the following compiler error:
Nested function cannot capture inout parameter and escape
So it really just looks like a corner case that hasn’t been optimised yet. Even in an -O build, although the heap allocation for the Bar
instance is able to be optimised to a stack allocation for just the foo
property, this still results in an unnecessary second reference to the Foo
instance.
Solutions
One solution would be to just add an inout
self
parameter to nestedFunc()
, allowing self
to just be passed by reference, rather than being captured:
func nestedFunc(_ `self`: inout Bar) {
_ = self // do something useful with self
}
// ...
nestedFunc(&self)
which now generates the SIL (-Onone):
// function_ref Bar.(bar() -> ()).(nestedFunc #1)(inout Bar) -> ()
%5 = function_ref @main.Bar.(bar () -> ()).(nestedFunc #1) (inout main.Bar) -> () : $@convention(thin) (@inout Bar) -> (), loc "main.swift":31:9, scope 10 // user: %6
%6 = apply %5(%0) : $@convention(thin) (@inout Bar) -> (), loc "main.swift":31:25, scope 10
The advantage of this solution is that it's just a straightforward pass-by-reference (as the Bar
parameter is marked @inout
). Because of this, there only ever exists one copy of the instance of Bar
– thus isKnownUniquelyReferenced(_:)
can return true.
Another possible solution, if self
isn’t mutated within nestedFunc()
, is to pass self
by value, rather than reference. This can be done with a capture list in a local closure:
let nestedFunc = { [`self` = self] in // copy self into the closure.
_ = self // the self inside the closure is immutable.
}
// ...
nestedFunc()
The advantage is that you don’t need to explicitly pass anything into the nestedFunc()
call. Because the instance of Bar
isn’t passed by value until the closure creation – it won’t interfere with the call to isKnownUniquelyReferenced(_:)
, assuming that the call precedes the closure creation.
Function inside Function retain cycle
Just assign your closure to a local variable. At that point, extracting out dismiss
is pointless, so just inline it:
private func setupDismissCallbacks() {
let dismissCallback: () -> Void = { [weak self] in
guard let self = self else { return }
self.videoExporter?.cancel()
self.rootViewController.dismiss(animated: true, completion: nil)
self.delegate?.childCoordinatorDidFinish(self)
}
saveModalViewController.onButtonDismiss = dismissCallback
saveModalViewController.onDimmedAreaDismiss = dismissCallback
}
python: is it legal to pass self to the nested function inside the class method?
Any function defined within a scope can use variables from its enclosing scope. In this case, you're defining a function within a function, so all the variables in the enclosing function are available - self, param1, and param2
. So you can pass self
as a parameter to the inner function, but since it already has access to self
, there's not much point to doing so.
If you create new variables with these names inside your new function, they "shadow" the variables with those names in the outer scope, meaning the names from the outer scope are inaccessible as long as the same names refer to something else in the inner scope. self
is just a normal variable name with no special meaning, except that it's the first parameter of a class's instance function, and thus gets passed implicitly when the method is called via the dot operator - meaning that it's subject to these rules.
In your inner function, you're passing self
explicitly, which is harmless but also pointless in this case. Python 3.6 (and Pycharm) is giving you warnings because it can't do typechecking on this, but that's all.
In other words, this would work just as well and cause no errors:
def func1(self, param1, param2):
def inner_func1():
print(self, self.a, self.b)
inner_func1()
Use [weak self] in the nested block in swift
If you don't use [weak self], then for the life time of that block, you can have a circular reference between it and the object self refers to, when an object loses a reference to it, its reference drops by one, when it reaches zero then it is deallocates and reduces the reference count of any object it has a reference to. If you have a circular reference then neither are going to reach zero, because neither is going to get to zero to deallocate itself and reduce the reference to the other. For regular objects this is a problem because they will never be deallocated, for block though, it can depend on how they are used, if they are passed to a function that uses them straight away, then once they are executed they will be deallocated, and any circular references will be cut, it may be even beneficial that whilst your block is executing that it and self can't go away, but if the block is retained as an instance variable to be called, then you have a circular reference that will never go away. The way to deal with is is to use [weak self], saying references to self are weak in this block, you can then just deal with that on each time you use it, self?.myFunction() for example, or you can create a strong reference at the beginning, it used to be you used to have to use a different variable to self, but now you can go
guard let self = self else { return }
one of the good things of doing it this way, you are saying if it gets this far I want the block to execute completely, you have created a circular reference for the execution of the block and it will not go away until the block finishes executing. For example if you have a few functions starting with self?, then mid way through your block executing self could become nil, and only the first few functions are executed and the later are not.
Do capture lists of inner closures need to redeclare `self` as `weak` or `unowned`?
The [weak self]
in anotherFunctionWithTrailingClosure
is not needed.
You can empirically test this:
class Experiment {
func someFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}
func anotherFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}
func doSomething() {
print(#function)
}
func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
And then:
func performExperiment() {
DispatchQueue.global().async {
let obj = Experiment()
obj.testCompletionHandlers()
// sleep long enough for `anotherFunctionWithTrailingClosure` to start, but not yet call its completion handler
Thread.sleep(forTimeInterval: 1.5)
}
}
If you do this, you will see that doSomething
is never called and that deinit
is called before anotherFunctionWithTrailingClosure
calls its closure.
That having been said, I might still be inclined to use the [weak self]
syntax on anotherFunctionWithTrailingClosure
to make my intent explicit.
How to perform selector with a nested function?
instead, you can try below code to achieve your goal
class MyClass {
let button = UIButton()
@objc public func parentFunc(_ sender : UIButton?)
{
func childFunc() {
print("User tapped")
}
if sender != nil && sender.tag == 100{
childFunc()
return
}
button.addTarget(self, action: #selector(parentFunc(_:)), for: .touchUpInside)
button.tag = 100
}
}
In above code, Sender is optional so you can pass nil when you don't want to call child function like parentFunc(nil)
Related Topics
Add Placeholder to Uitextfield, How to Set the Placeholder Text Programmatically in Swift
Get Playground to Display All Loop Results
Xcode Error: Missing Required Module 'Firebase'
How to Get Current Location with Swiftui
Ios9: Using Dynamic Framework with Simulator and Device
iOS - Arkit Node Disappear After 100M
Finding the Difference in Time Between Two Nsdates Swift
Code Migration from Swift 2.X to Swift 4
Using Foreach with a an Array of Bindings (Swiftui)
Difference Between Switch Cases "@Unknown Default" and "Default" in Swift 5
Insert, Update and Delete Animations with Foreach in Swiftui
How to Rounded the Corners When I Draw Rectangle Using Uibezierpath Points
How to Make a Local Module with the Swift Package Manager
Trying to Use Keychainitemwrapper by Apple "Translated" to Swift
Accessing Multiple Audio Hardware Outputs/Channels Using Avfoundation and Swift