Is [Weak self] always needed when dealing with URLSession? - ios

I can't figure out if I need to use [weak self] in this situation or not ?
HTTPClient.swift:
struct HTTPClient {
let session = URLSession.shared
func get(url: URL, completion: #escaping (Data) -> Void) {
session.dataTask(with: url) { data, urlResponse, error in
completion(data) // assume everything will go well
}.resume()
}
}
Service.swift
struct Service {
let httpClient: HTTPClient
init(httpClient: HTTPClient = HTTPClient()) {
self.httpClient = httpClient
}
func fetchUser(completion: #escaping (User) -> Void) {
httpClient.get("urlToGetUser") { data in
// transform data to User
completion(user)
}
}
}
ViewModel.swift
class ViewModel {
let service: Service
let user: User?
var didLoadData: ((User) -> Void)?
init(service: Service) {
self.service = service
loadUser()
}
func loadUser() {
service.fetchUser { [weak self] user in // is [weak self] really needed ?
self?.user = user
self?.didLoadData?(user)
}
}
}
is it really needed here to user [weak self] ? and is there a rule on how to check if it's needed in general when we deal with an API that we don't know what's happening to the closure ? or that does not matter (it's up to us to decide) ?

In the example you've given, [weak self] is potentially unnecessary. It depends on what you want to happen if ViewModel is released before the request completes.
As noted in the URLSessionDataTask docs (emphasis mine):
After you create a task, you start it by calling its resume() method. The session then maintains a strong reference to the task until the request finishes or fails; you don’t need to maintain a reference to the task unless it’s useful for your app’s internal bookkeeping.
The session has a strong reference to the task. The task has a strong reference to the closure. The closure has a strong reference to the ViewModel. As long as the ViewModel doesn't have a strong reference to the task (which it doesn't in the code you provided), then there's no cycle.
The question is whether you want to ensure that the ViewModel continues to exist long enough for the closure to execute. If you do (or don't care), then you can use a simple strong reference. If you want to prevent the task from keeping the ViewModel alive, then you should use a weak reference.
This is how you need to think about reference cycles. There is no general rule "use weak here." You use weak when that's what you mean; when you don't want this closure to keep self around until it is released. That particularly is true if it creates a cycle. But there's no general answer for "does this create a cycle." It depends on what pieces hold references.
This also points to where your current API design is not as good as it could be. You're passing didLoadData in init. That very likely is going to create reference cycles and force your caller to use weak. If instead you made didLoadDdata a completion handler on loadUser(), then you could avoid that problem and make life easier on the caller.
func loadUser(completion: #escaping ((User?) -> Void)? = nil) {
service.fetchUser {
self?.user = user
didLoadData?(user)
}
}
(Your current API has a race condition in any case. You're starting loadUser() before didLoadData can be set. You may be assuming that the completion handler won't complete before you've set dataDidLoad, but there's no real promise of that. It's probably true, but it's fragile at best.)

The problem with your code is not with the use of URLSession but with the fact that you are retaining a function in your view controller:
class ViewModel {
var didLoadData: ((User) -> Void)?
}
If the didLoadData function mentions self (i.e. the ViewModel instance) implicitly or explicitly, you have a retain cycle and a memory leak unless you say weak self or unowned self.

Related

RxSwift. Execute separate Observables sequently

I'm trying to achieve my Observables to execute only when previous Observable has completed. I can't use flatMap, because subscriptions can be called from different places, and this Observables is not connected with each other. To be specific: I have my CollectionView loading more content from server and 2 seconds after that user clicks "Send comment" button while CollectionView is still loading its batch. So I want to wait until CollectionView update completes and only then execute my comment's posting request. I created a class named ObservableQueue and it's working just fine. But I need to know if it has issues like memory leaks, dead locks or maybe I just missing something. Here it is:
extension CompositeDisposable {
#discardableResult
func insert(disposeAction: #escaping () -> ()) -> DisposeKey? {
return insert(Disposables.create(with: disposeAction))
}
}
class ObservableQueue {
private let lock = NSRecursiveLock()
private let relay = BehaviorRelay(value: 0)
private let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "ObservableQueue.scheduler")
func enqueue<T>(_ observable: Observable<T>) -> Observable<T> {
return Observable.create({ observer -> Disposable in
let disposable = CompositeDisposable()
let relayDisposable = self
.relay
.observeOn(self.scheduler)
.filter({ value -> Bool in
if value > 0 {
return false
}
self.lock.lock(); defer { self.lock.unlock() }
if self.relay.value > 0 {
return false
}
self.relay.accept(self.relay.value + 1)
disposable.insert {
self.lock.lock(); defer { self.lock.unlock() }
self.relay.accept(self.relay.value - 1)
}
return true
})
.take(1)
.flatMapLatest { _ in observable }
.subscribe { observer.on($0) }
_ = disposable.insert(relayDisposable)
return disposable
})
}
}
And then I can use it like this:
let queue = ObservableQueue()
...
// first observable
let observable1 = Observable
.just(0)
.delay(5, scheduler: MainScheduler.instance)
queue
.enqueue(observable1)
.subscribe(onNext: { _ in
print("here1")
})
.disposed(by: rx.disposeBag)
// second observable
let observable2 = Observable
.just(0)
.delay(5, scheduler: MainScheduler.instance)
queue
.enqueue(observable2)
.subscribe(onNext: { _ in
print("here2")
})
.disposed(by: rx.disposeBag)
// third observable
let observable3 = Observable
.just(0)
.delay(5, scheduler: MainScheduler.instance)
queue
.enqueue(observable3)
.subscribe(onNext: { _ in
print("here3")
})
.disposed(by: rx.disposeBag)
CLGeocoder has the same issue. According to the documentation, you can't call one of the geocoder methods while it's working on a previous request so very much like what you are trying to do. In this gist (https://gist.github.com/danielt1263/64bda2a32c18b8c28e1e22085a05df5a), you will find that I make the observable calls on a background thread and protect the job with semaphore. That's the key, you need a semaphore, not a lock.
Something like this should work for you:
class ObservableQueue {
private let semaphore = DispatchSemaphore(value: 1)
private let scheduler = ConcurrentDispatchQueueScheduler(qos: .userInitiated)
func enqueue<T>(_ observable: Observable<T>) -> Observable<T> {
let _semaphore = semaphore // To avoid the use of self in the block below
return Observable.create { observer in
_semaphore.wait()
let disposable = observable.subscribe { event in
switch event {
case .next:
observer.on(event)
case .error, .completed:
observer.on(event)
}
}
return Disposables.create {
disposable.dispose()
_semaphore.signal()
}
}
.subscribeOn(scheduler)
}
}
I will give you some suggestions that I think will help you in the future.
Avoid as much as possible the Observable.create, this is the "brute force" creation of an observable and it doesn't handle back pressure at all, you'll have to implement it yourself, and it's not something easy.
Usually for HTTP api calls, you don't need Observable, you should use Single or Completable since you expect only one response from your server, not a stream of responses.
You should be careful with strong self inside the onNext/on..., as a rule of thumb if the class that subscribes to the observer has the dispose bag, you should use a weak self.
Now for your particular case, if you need to just this pair of observers (fetch & send comment), I think the queue is a little bit overkill. You can simply call the post comment observer (if available) on the do(onNext:) method of your "fetch" observer. Do on next is called every time an "onNext" event is triggered.
If you still need a queue, I would go with an OperationQueue that enqueues only operations and has a method like observeOperationchanges() -> Observeble<Operation> this will be triggered every time an operation is completed. In this way you subscribe once and enqueue multiple times, but this might not fit your needs.
I would use .combineLatest() to produce an event once both observables have emitted something. See http://rxmarbles.com/#combineLatest

Should I weakify "local" variables used in a block?

This is not another question about [weak self]. This is about use of variables not contained by self, but rather by the wrapping function.
func someFunction(){
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff()
})
}
As far as I understand, I need the [weak self] in order to prevent a retain cycle.
But what if I need to use a variable from the wrapping function, like this:
func someFunction(){
let someVariable = MyObject()
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff(with: someVariable)
})
}
This works, which makes me wonder.. How, and how long is someVariable held in memory? Can it create its own tiny retain cycle where my completion block strongly references the local someVariable? How will they be released? Should I add [weak self, weak someVariable] in the block? But then, won't someVariable be released immediately after I call someOtherFunction, because it's the end of this function - and the end of someVariable's lifetime..?
I'm having trouble completely understanding references, and can't see how my completionBlock and someVariable will ever be released.. Are blocks even released?
Any variable referenced inside a closure will be strongly retained by that closure. You can adjust that by including a closure capture list (e.g. [weak self]), which allows you to specify the particular memory management pattern of references captured in the closure.
func someFunction(){
let someVariable = MyObject()
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff(with: someVariable)
})
}
Here, someVariable is retained by the closure, as you have stated. In this case, it has to be, because nobody else cares about it. As you've mentioned in comments, if you used a capture list of [weak someVariable] then it would always be nil when the completion block executed, as it has gone out of scope in it's original function.
A "tiny retain cycle" isn't being created. A retain cycle has to be a cycle - that is, A holds a strong reference to B, which holds a strong reference to A. someVariable doesn't have references to anything.
Once someOtherFunction has finished with the reference to the completion closure, everything goes away. The closure is just another variable as far as someOtherFunction is concerned, and it will survive as long as it is in scope.
Should I weakify “local” variables used in a block? - no, as they will then be nil by the time the block comes to use them.
I would like to mention not so clear option, what is very usual place to create retain cycle and where you should be aware of weaken variables.
Lets consider this situation:
func someFunction() {
let object = Something()
object.handler = { [weak self] in
self?.doStuff(with: object)
}
}
Now there is retain cycle, and object cannot be deallocated until somebody manually unset the handler. Because now object strongify itself in the captured block.
So better solution is:
func someFunction() {
let object = Something()
object.handler = { [weak self, unowned object] in
self?.doStuff(with: object)
}
}
And good practice is pass the object as argument in handler
func someFunction() {
let object = Something()
object.handler = { [weak self] (object) in
self?.doStuff(with: object)
}
}
So signature of this should look like:
class Something {
var handler:((Something) -> Void)?
deinit {
print("retain cycle is not here")
}
}

Swift: does capturing a function parameter, within a closure, hold it strongly?

Capturing a function parameter within a closure
func someFunction(tableView: UITableView) -> (() -> ()) {
return { [weak self] in
self?.someOtherFunction() {
tableView.performTask()
}
}
}
If so, what is the syntax for ensuring the tableView parameter is weak, in the above example?
Closure capture parameters strongly by default. In most common cases in closure you accessing to properties declared in self, without capturing other references, so making self weak is enough. In your case you need expand closure capture list and include tableView to it:
func someFunction(tableView: UITableView) -> (() -> ()) {
return { [weak self, weak tableView] in
self?.someOtherFunction() {
tableView?.performTask()
}
}
}
You may read more about capture lists in ARC documentation:
You resolve a strong reference cycle between a closure and a class
instance by defining a capture list as part of the closure’s
definition. A capture list defines the rules to use when capturing
one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each
captured reference to be a weak or unowned reference rather than a
strong reference.

Correct placement of capture list in nested closures in swift

Where do I define captured references for nested closures in Swift?
Take this code as an example:
import Foundation
class ExampleDataSource {
var content: Any?
func loadContent() {
ContentLoader.loadContentFromSource() { [weak self] loadedContent in
// completion handler called on background thread
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.content = loadedContent
}
}
}
}
class ContentLoader {
class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) {
/*
Load content from web asynchronously,
and call completion handler on background thread.
*/
}
}
In this example, [weak self] is used in both trailing closures, however the compiler is perfectly happy if I omit [weak self] from either one of the trailing closures.
So that leaves me 3 options for defining my capture list:
define captures on every nested closure leading up to the reference
define captures on the first closure only.
define captures on only the most nested closure that actually uses the reference.
My question is:
If I know that my ExampleDataSource could be nil at some point, what is the best option to go with?
It is important to note that GCD dispatch_async will NOT cause a retain cycle. In other words, when the block has finished executing, GCD will not retain any references made within the block.
The same is not true for strong references between classes, or strong references within a closure assigned to a property of an instance. Apple Documentation
That being said, in this example, the correct answer is option 2, to define captures on the first closure only.
For testing purposes I modified the code slightly:
class ExampleDataSource {
init() {
print("init()")
}
deinit {
print("deinit")
}
var content: Any?
func loadContent() {
print("loadContent()")
ContentLoader.loadContentFromSource() { [weak self] loadedContent in
dispatch_async(dispatch_get_main_queue()) {
print("loadedContent")
self?.content = loadedContent
}
}
}
}
class ContentLoader {
class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
sleep(5) // thread will hang for 5 seconds
completion(loadedContent: "some data")
}
}
}
First I create, var myDataSource: ExampleDataSource? = ExampleDataSource().
Then I run myDataSource.loadContent().
Before the completion handler gets a chance to run, I set myDataSource = nil, removing all references to it.
The debug console indicates that a reference to self was not retained:
init()
loadContent()
deinit
loadedContent
Looks like we found our answer! But just for completion's sake, let's test the alternatives...
If [weak self] is instead captured on only the inner most trailing closure, GCD will retain ExampleDataSource until the block has finished executing, which explains why the debug would instead look like this:
init()
loadContent()
loadedContent
deinit
The same thing will happen if no capture list is included and we never optionally unwrapped self, although the compiler, does try to warn you!
While it isn't technically incorrect to include [weak self] captures in all trailing closures, it does detract from the readability of code and doesn't feel very 'Swift-like'.

How to set a weak reference to a closure/function in Swift?

In HMSegmentedControl, I'd like to set the segmentedControl.indexChangeBlock to an instance method to handle the action.
The official example is: https://github.com/HeshamMegid/HMSegmentedControl/blob/master/HMSegmentedControlExample/HMSegmentedControlExample/ViewController.m (Line 63 ~ 68), but that's Objective-C.
In Swift, functions are first class citizens. So I wanna set an instance method to this block property.
But my code would lead to a circular reference, it seems that I should define a weak reference:
class ExampleVC: UIViewController {
var segmentedControlIndex: Int = 0
override func viewDidLoad() {
let segmentedControl3 = HMSegmentedControl(sectionImages: ... , sectionSelectedImages: ... )
segmentedControl3.frame = ...
segmentedControl3.indexChangeBlock = someInstanceMethod
}
func someInstanceMethod(index: Int) {
segmentedControlIndex = index
}
}
However, I cannot define a weak reference to a non-class type. What can I do? Is it legal to do this?
[unowned self] is dangerous. What this does is to tell the runtime 'Assume self has not been released, don't worry about checking.' If self does get released in the meantime, your application will SEGFAULT.
In this case, { [weak self] in self?.someInstanceMethod($0) } will break the reference cycle, and turns into a no-op if self is released.
[weak self] is always safe, while [unowned self] can introduce app crashes if you modify other areas of your code (like view hierarchy, for example)...
Instead of defining weak reference to the closure, you should use "Capture List" in the closure.
segmentedControl3.indexChangeBlock = { [unowned self] in self.someInstanceMethod($0) }
As far as I know, this is the only way to avoid strong reference cycles.

Resources