Swift, why don't class methods need closure lists - ios

If functions are essentially closures. Why don't methods of a class need closure lists when referencing self or another instance property within the closure.
Is there a [unowned self] behind the scenes? For example:
class MyClass{
func myFunc(){
self.otherFunc()
}
func otherFunc(){
print()
}
}
Wouldn't there be a reference cycle within myFunc? Ie, the closure is pointing to self, and the instance is pointing to the function. Neither could be deallocated.

"If functions are essentially closures." This isn't true. Functions (and methods) are not the same thing as closures. Functions have all their free variables unbound. Closures have bound some or all of their free variables (closed over them, which is where the name "closure" comes from).
A "free variable" is any variable defined outside the scope of the function (including its formal parameters). The top-level function func f(x: Int) has one free variable; when you call it, you must pass a parameter. A closure like { f(1) } has no free variables. When you call it, you do not pass any parameters.
A method, like a function, does not capture anything. It is passed all of its free variables when it is executed. For example, when you make the call object.doThis(), this is the same as calling Type.doThis(object)().
class X {
func doThis() {}
}
let x = X()
x.doThis()
X.doThis(x)() // Same thing
X.doThis(x) is a function that returns a function. There's no magic here. All the free variables are provided during the call. Nothing is captured. (The "free variable" in the case you describe is self, but that doesn't change anything. self is not special, except that it gets a little syntactic sugar around it.)
This is different than a closure:
let c = { x.doThis() }
c()
When I call c(), how does it know the value of x? I may have returned c and x may be out of scope now. The system has to keep track of x (including making a strong reference so it doesn't deallocate), and it does that by capturing it, or "closing over x" which raises the possibility of retain loops. So in c, x is bound. It is not free. You can't pass it when you call c().
self is not special here. It's just another variable. [weak self] in closures isn't special either. You can write [weak x] just as well. The [...] syntax is just the capture list.

Closures may only cause reference cycles when the closure is kept alive. Consider this:
let foo = MyClass()
let bar: () -> () = { in
print(foo)
}
The bar closure holds a reference to foo, but that reference goes away once nothing references bar anymore. For instance:
func f(foo: MyClass) {
let bar: () -> () = { () in
print(foo)
}
}
This does not create a reference cycle, because when f returns, the closure in barĀ is destroyed. Similarly, when you call myFunc and otherFunc, you do need a strong reference to self (the compiler ensures that you have it), but as you no longer need it at the end of the function, no cycle is created.
In general, a closure will not systematically create a reference cycle, even if it is #escaping. Consider the case of Dispatch.async:
class MyClass {
func foo() {
DispatchQueue.main.async {
print(self)
}
}
}
This does not actually create a reference cycle, because even though the closure references self for a while, self does not reference the closure.
The dangerous case is this one:
class MyClass {
var closure: () -> ()
func f() {
self.closure = {
print(self)
}
}
}
This one actually creates a reference cycle: self.closure has a strong reference to self, and self has a strong reference to self.closure.

Related

Swift capture self in closure

I have the following Swift code:
func callback(_ status: PHAuthorizationStatus) {
}
func myFunction() {
let handler:(PHAuthorizationStatus) -> Void = { (status) in
self.callback(status)
}
PHPhotoLibrary.requestAuthorization(for: .addOnly) { (status) in
handler(status)
}
}
What my doubt is whether there is a retain cycle in closures declared as local variable and whether we should use unowned or weak self instead inside handler. How does Swift handle closures declared as local var inside function?
There are two possibilities: Either the function that you call can only call the closure while the function itself is running, then the local variable keeps the closure alive.
Or the function that you call will save the closure away somewhere, and the closure may be called after the function has returned. In that case, the function will declare its closure parameter as "#escaping" and that will keep it alive.

Closure cannot implicitly capture a mutating self parameter

I am using Firebase to observe event and then setting an image inside completion handler
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
However I am getting this error
Closure cannot implicitly capture a mutating self parameter
I am not sure what this error is about and searching for solutions hasn't helped
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:) is most likely a value type (a struct?), in which case a mutating context may not explicitly capture self in an #escaping closure.
The simple solution is to update your owning type to a reference once (class).
The longer version
The observeSingleEvent(of:with:) method of Firebase is declared as follows
func observeSingleEvent(of eventType: FIRDataEventType,
with block: #escaping (FIRDataSnapshot) -> Void)
The block closure is marked with the #escaping parameter attribute, which means it may escape the body of its function, and even the lifetime of self (in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: #escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
SE-0035: Limiting inout capture to #noescape contexts
Stating [emphasis mine]:
Capturing an inout parameter, including self in a mutating
method, becomes an error in an escapable closure literal, unless the
capture is made explicit (and thereby immutable).
Now, this is a key point. For a value type (e.g. struct), which I believe is also the case for the type that owns the call to observeSingleEvent(...) in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).
The simplest solution to this issue would be making the type owning the observeSingleEvent(...) a reference type, e.g. a class, rather than a struct:
class Foo {
init() {}
private func bar(with block: #escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
Just beware that this will capture self by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly capture self weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
Sync Solution
If you need to mutate a value type (struct) in a closure, that may only work synchronously, but not for async calls, if you write it like this:
struct Banana {
var isPeeled = false
mutating func peel() {
var result = self
SomeService.synchronousClosure { foo in
result.isPeeled = foo.peelingSuccess
}
self = result
}
}
You cannot otherwise capture a "mutating self" with value types except by providing a mutable (hence var) copy.
Why not Async?
The reason this does not work in async contexts is: you can still mutate result without compiler error, but you cannot assign the mutated result back to self. Still, there'll be no error, but self will never change because the method (peel()) exits before the closure is even dispatched.
To circumvent this, you may try to change your code to change the async call to synchronous execution by waiting for it to finish. While technically possible, this probably defeats the purpose of the async API you're interacting with, and you'd be better off changing your approach.
Changing struct to class is a technically sound option, but doesn't address the real problem. In our example, now being a class Banana, its property can be changed asynchronously who-knows-when. That will cause trouble because it's hard to understand. You're better off writing an API handler outside the model itself and upon finished execution fetch and change the model object. Without more context, it is hard to give a fitting example. (I assume this is model code because self.img is mutated in the OP's code.)
Adding "async anti-corruption" objects may help
I'm thinking about something among the lines of this:
a BananaNetworkRequestHandler executes requests asynchronously and then reports the resulting BananaPeelingResult back to a BananaStore
The BananaStore then takes the appropriate Banana from its inside by looking for peelingResult.bananaID
Having found an object with banana.bananaID == peelingResult.bananaID, it then sets banana.isPeeled = peelingResult.isPeeled,
finally replacing the original object with the mutated instance.
You see, from the quest to find a simple fix it can become quite involved easily, especially if the necessary changes include changing the architecture of the app.
If someone is stumbling upon this page (from search) and you are defining a protocol / protocol extension, then it might help if you declare your protocol as class bound. Like this:
protocol MyProtocol: class {
...
}
You can try this! I hope to help you.
struct Mutating {
var name = "Sen Wang"
mutating func changeName(com : #escaping () -> Void) {
var muating = self {
didSet {
print("didSet")
self = muating
}
}
execute {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 15, execute: {
muating.name = "Wang Sen"
com()
})
}
}
func execute(with closure: #escaping () -> ()) { closure() }
}
var m = Mutating()
print(m.name) /// Sen Wang
m.changeName {
print(m.name) /// Wang Sen
}
Another solution is to explicitly capture self (since in my case, I was in a mutating function of a protocol extension so I couldn't easily specify that this was a reference type).
So instead of this:
functionWithClosure(completion: { _ in
self.property = newValue
})
I have this:
var closureSelf = self
functionWithClosure(completion: { _ in
closureSelf.property = newValue
})
Which seems to have silenced the warning.
Note this does not work for value types so if self is a value type you need to be using a reference type wrapper in order for this solution to work.

Why doesn't referencing outside of a function create retain cycle?

Sorry, but I know this is a really dumb question, and I already kind of 'know' the answer, but I need someone to clearly explain to me WHY the answer is what it is.
Lately, I've become a bit obsessed/paranoid about retain cycles and memory leaks in my code, after going through some nightmarish debugging with various memory issues, so in the future I want to nip them in the bud. But after reading and learning a lot about ARC and retain cycles in Swift, although it makes sense, I still don't really have enough of an "intuitive" or natural feel for it, to feel confident that I could spot one, or the lack of one, as I'm coding. So I'm starting to become a little paranoid that I'm creating retain cycles with even basic stuff without realizing it.
So, with that in mind, why doesn't any ordinary function that uses a variable declared outside of it create a retain cycle? For example:
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
In this case, self.moreLetters references the function addLetters, and then the constant self.a is references from within the function addLetters. So would this create a retain cycle if I don't capture weak/unowned self? It seems absurd to me that something this simple would cause a problem...or is it? What about in a nested function, like this:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
Would that also create a retain cycle? (Yeah I know this is a convoluted way of performing a simple task; I'm just using this code as an example to make my point).
Have I become super-paranoid and am severely overthinking things?
First, you need to understand how a basic retain cycle is formed. A retain cycle is formed when an object A refers to an object B strongly and at the same time. object B refers to object A strongly as well.
Let's look at your first bit of code.
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
Actually, a class by itself can never create retain cycles, so let's add some code to create an object:
var obj = someClass()
First, a is initialized to "I'm letter a". After that, moreLetters is initialized by calling the method addLetters. After the method returns, moreLetters is initialized to "I'm letter abcdefg". So far so good.
Now we set obj to nil:
obj = nil
If a retain cycle were formed, obj would not be deinitialized. However, in actuality, obj is deinitialized because nothing holds a strong reference to obj!
"Wait a minute!" you say, "But the method addLetters still refers to someClass because it has a in it!" Well, in fact, addLetters has already returned! Therefore, everything in it doesn't matter anymore! In addition, addLetters belongs to obj, which you have already set to nil!
Now let's look at your second code:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
A retain cycle does not form because there isn't a even a reference type! There is no objects to be created. All you did in the second code is playing with strings, which are value types. Even if there were a class, a retain cycle would not form because as I said, when you set obj to nil, all the methods in it "disappear" because methods belong to objects.
What about those closures where I must write [weak self] or a retain cycle forms?
Those closures are escaping closures. Normal closures are deinitialized after they return. However, escaping closures are retained by some object so they are not deinitialized immediately. For more info, see Escaping Closures in Swift

Can I omit self when accessing instance variables in closure of map or reduce method?

I know I should use self in closure to access instance variables and Xcode shows error if I don't, but I didn't get error when I omitted self in closure of reduce method.
Can I omit self in that case?
case A
gridsCount = (0..<collectionView!.numberOfSections()).reduce(0) { [weak self] (sum, section) -> Int in
return sum + self.gridsInSection(section)
}
case B
gridsCount = (0..<collectionView!.numberOfSections()).reduce(0) { (sum, section) -> Int in
return sum + gridsInSection(section)
}
The reason why you can omit self in the block lays in the existence of #noescape keyword in the function declaration:
#warn_unused_result #rethrows func reduce<T>(initial: T,
#noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> T
From Xcode 6 release notes:
A new #noescape attribute may be used on closure parameters to functions. This indicates that the parameter is only ever called (or passed as an #noescape parameter in a call), which means that it cannot outlive the lifetime of the call.
This enables some minor performance optimizations, but more importantly disables the self. requirement in closure arguments.
#noescape prevents self from being retained which is perfectly OK for using the closure to run that in a synchronous manner where you are sure that self is going to be alive in the course of the execution.
If your closure is going to be used as a completion block for some kind of asynchronous operations such as network requests, retrieving media files from gallery etc. where you heavily need self to be retained and stay alive inside the block, you don't mark it with #noescape.

Concern about memory when choosing between notification vs callback closure for network calls?

Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.

Resources