Why is this a retain cycle? - ios

I have basic understanding of ARC but in the following example I suddenly get really confused.
FeedViewController has a strong reference of NetworkHelper, then the NetworkHelper has a function which takes a closure and call it later.
So here's the confusion:
the closure is passed from FeedViewController to NetworkHelper, And this block is not being retained inside NetworkHelper, so why does NetworkHelper has a strong reference of NetworkHelper? this is stated in an article but I just could't figure out why. It makes sense to me only if NetworkHelper keep a strong reference to the block.
class NetworkHelper {
func getFeed(completion: #escaping ([FeedItem]) -> Void) {
Alamofire.request(…).responseJSON { (response) in
if let value = response.result.value {
if let json = JSON(value)[Constants.items].array {
completion(json.flatMap(FeedItem.init))
}
}
}
}
}
class FeedViewController {
var tableView: UITableViewController
var feedItems: [FeedItem]
var networkHelper: NetworkHelper
override func viewDidLoad() {
...
networkHelper.getFeed() { items in
self.feedItems = items
self.tableView.reloadData()
}
}
}

Technically, there is no cycle.
First of all, NetworkHelper never owns anything, it just passes a closure to Alamofire.
Alamofire holds to that closure, which retains a FeedViewController instance (as self). However, Alamofire is not owned by FeedViewController, therefore there is no cycle.
It's true that while the request is running, FeedViewController cannot be deallocated because the completion callback prevents that, but that could be an expected behavior and there is definitely no ownership cycle.

Related

Are there problems with strong retain issues with code such as this:

In the following code, is there a problem with the self in self.tableView.reloadData() being a strong reference, and should it be changed to be weak, or is it ok as it is?
class SomeViewController : UIViewController {
fileprivate var notificationToken: NotificationToken? = nil
...
override func viewDidLoad()
{
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(onNotification), name: NSNotification.Name(SomeNotification), object: nil)
realmNotificationToken = blockedList.observe({ (changes: RealmCollectionChange) in
switch changes
{
case .initial:
self.tableView.reloadData() // Case 1
break
...
#objc func onNotification()
{
DispatchQueue.main.async{
self.tableView.reloadData() // Case 2
}
}
In both of these examples are there any problems with retain cycles due to the fact self is a strong reference, and it should change to weak?
In both instances, what is the life time of the two blocks within which self.tableView.reloadData() is being used? If the lifetime is ephemeral then there's no problems with using strong, are these blocks however long lived?
Case 2 is a non escaping closure so there is no issue.
Case 1 is #escaping so you need to use a weak or unowned reference. #escaping means you are passing the closure to something that may outlive the object that created the closure (ie you ViewController). If you strongly capture self in the #escaping then your ViewController now lives for as long as the closure lives, and the closure lives until you cancel your subscription. If you only cancel the subscription when the ViewController deintializes then you now have a cycle. The closure can never be released because the subscription never dies and the ViewController can never die because the subscription never dies.
EDIT:
I should add you actually don't need self in either case; you can simply capture the tableview (if its implicitly unwrapped it will be desugared into a plain optional, which is fine):
realmNotificationToken = blockedList.observe({ [tableView] (changes: RealmCollectionChange) in
switch changes {
case .initial:
tableView?.reloadData()

Delegate becomes nil in Operation of urlSession. How to keep delegate variable in separate thread?

I'm using an OperationQueue to upload files one by one to a remote server using URLSession.dataTask. A delegate is used to update a progress bar but after implementing OperationQueue my delegate becomes nil. It worked without OperationQueues. Looking at the stack while the program is running I don't see my progress bar's view controller. It's been a few days and I still can't quite figure it out. I'm guessing the view controller is getting deallocated but I'm not sure how to prevent it from getting deallocated. Thank you.
I have my delegate set to self in NetWorkViewController but inside my NetworkManager class's urlSession(didSendBodyData), the delegate becomes nil. The delegate is not weak and is a class variable.
However, my delegate becomes none-nil again within the completion block of my BlockOperation. This works for dismissing the ViewController via delegation. But the delegate is nil when I'm trying to update inside urlSession(didSendBodyData)...
UPDATE 10/30/2018
It seems that my urlSessions delegates are on a separate thread and is enqueued to the main thread when called but I lose reference to my custom delegate that updates the UI. I'm trying to read more about multithreading but any help would be appreciated!
UPDATE 2 10/30/2018
Solution found The issue was that I was creating another instance of NetworkManager inside each operation. This causes the delegate to be nil because a new instance of NetworkManager is being created for each operation. The fix is to pass self from the original NetworkManager so the delegate is retained.
uploadFiles
func uploadFiles(item: LocalEntry) {
let mainOperation = UploadMainFileOperation(file: item)
// This is where I need to give the operation its
// networkManager so the proper delegate is transferred.
mainOperation.networkManager = self
mainOperation.onDidUpload = { (uploadResult) in
if let result = uploadResult {
self.result.append(result)
}
}
if let lastOp = queue.operations.last {
mainOperation.addDependency(lastOp)
}
queue.addOperation(mainOperation)
....
....
let finishOperation = BlockOperation { [unowned self] in
self.dismissProgressController()
for result in self.result {
print(result)
}
self.delegate?.popToRootController()
}
if let lastOp = queue.operations.last {
finishOperation.addDependency(lastOp)
}
queue.addOperation(finishOperation)
queue.isSuspended = false
}
UploadMainFileOperation
class UploadMainFileOperation: NetworkOperation {
let file: LocalEntry
// First issue is here. I re-declared another NetworkManager that doesn't have its delegate properly set.
private let networkManager = NetworkManager()
// I have since have this class receive the original networkManager after it's declared.
var networkManager: NetworkManager?
var onDidUpload: ((_ uploadResult: String?) -> Void)!
init(file: LocalEntry) {
self.file = file
}
override func execute() {
uploadFile()
}
private func uploadFile() {
networkManager.uploadMainFile(item: file) {
(httpResult) in
self.onDidUpload(httpResult)
self.finished(error: "upload main")
}
}
}
urlSession(didSendBodyData)
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
// This is wrong.
let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
updateDelegateWith(progress: uploadProgress)
// This is the correct way for my situation.
// Because each operation on the queue is on a separate thread. I need to update the UI from the main thread.
DispatchQueue.main.async {
let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
self.updateDelegateWith(progress: uploadProgress)
}
}
updateDelegateWith(progress: Float)
func updateDelegateWith(progress: Float) {
delegate?.uploadProgressWith(progress: progress)
}
NetworkManagerViewController where the progress bar lives
class NetworkViewController: UIViewController, NetWorkManagerDelegate {
var localEntry: LocalEntry?
var progressBackground = UIView()
var progressBar = UIProgressView()
func uploadProgressWith(progress: Float) {
progressBar.progress = progress
view.layoutSubviews()
}
deinit {
print("deallocate")
}
override func viewDidLoad() {
super.viewDidLoad()
let networkManager = NetworkManager()
networkManager.delegate = self
networkManager.uploadFiles(item: self.localEntry!)
....
....
}
}
With the latest code shared, i would suggest to keep NetworkManager instance at the class level instead of a function level scope as this will ensure the networkManager instance is not deallocated.
class NetworkViewController: UIViewController, NetWorkManagerDelegate {
var localEntry: LocalEntry?
var progressBackground = UIView()
var progressBar = UIProgressView()
let networkManager = NetworkManager()
func uploadProgressWith(progress: Float) {
progressBar.progress = progress
view.layoutSubviews()
}
deinit {
print("deallocate")
}
override func viewDidLoad() {
super.viewDidLoad()
networkManager.delegate = self
networkManager.uploadFiles(item: self.localEntry!)
}
...
Also, you need to be careful for retain-cycles that cause memory leaks. To avoid retain cycles, you need to declare your delegate variable as weak.
As user #Kamran pointed out, I was creating a class level instance of networkManager inside UploadMainFileOperation. The issue has been fixed by changed that variable to an Optional and giving it an instance of NetworkManager, as self ,that was queueing up the operations. The code blocks as been updated with comments of the correct code along with the incorrect code.
If you set a delegate and later it becomes nil, this means your delegate has been deallocated.
I would recommend to create an (empty) deinit in your delegate class and set a breakpoint for the debugger in that method. This will help you find out where you're losing the reference to said delegate.
You can probably avoid this by assigning your delegate to a property of one of your classes or make it a strong reference in one of your completion blocks.

set properties on rectangle request. iOS, Swift

Im trying to set properties on a rectangle request.
Im trying to set minimum size, aspect ratio and others but get the error below.
Fatal error: Attempted to read an unowned reference but the object was already deallocated2018-08-13 16:08:09.081049+0100 app[4000:1277980] Fatal error: Attempted to read an unowned reference but the object was already deallocated
func performVisionRequest(image: CGImage, orientation: CGImagePropertyOrientation) {
DispatchQueue.global(qos: .userInitiated).async {
do {
let imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
try imageRequestHandler.perform(
[VNDetectRectanglesRequest(completionHandler:{ req, err in
self.rectanglesRequest(request:req, error:err)
})]
)
} catch let error as NSError {
self.sliceCompletion([UIImage]())
print("Failed to perform vision request: \(error)")
}
}
}
func rectanglesRequest(request: VNRequest, error: Error?) {
if let err = error as NSError? {
noRect = true
var slices = [imageNo]
self.sliceCompletion(slices as! [UIImage])
slices = []
print("Failed during detection: \(err.localizedDescription)")
return
}
unowned let rectanglesRequestSet = VNDetectRectanglesRequest()
rectanglesRequestSet.minimumSize = 0.07
rectanglesRequestSet.minimumAspectRatio = 0.2
rectanglesRequestSet.maximumAspectRatio = 0.3
rectanglesRequestSet.quadratureTolerance = 22.0
Here it is not good to mark rectanglesRequestSet as unowned, as it is causing it to be released before you can use it and when you attempt to message a released unowned object you will get a crash, which is explains the message you get:
Attempted to read an unowned reference but the object was already deallocated
unowned is used to break retain cycles which occur when a class's property holds a reference back to class itself, often through a closure. For example:
class ViewControllerA: UIViewController {
let viewControllerB = UIViewControllerB()
override func viewDidLoad() {
super.viewDidLoad()
viewControllerB.onSelect = {
self.onSelection()
}
}
func onSelection() {
// do something
}
}
class ViewControllerB: UIViewController {
var onSelect: (() -> Void)?
}
For example the code above creates a retain cycle as vcA -> vcB -> onSelect -> vcA, where vcA is an instance of ViewControllerA and vcB is an instance of ViewControllerB. vcA and vcB will never be released as vcA holds a reference to vcB by virtue of it being a property. And, vcB holds a reference to vcA by virtue of variable capture in the onSelect closure. This happens because in order for closures to execute code in the future they must hold a reference to all of the objects used in the closure, in the example vcA is the only object used so the closure holds a reference to it and vcB holds a reference to the closure. To prevent this retain cycle:
viewControllerB.onSelect = { [unowned self]
self.onSelection()
}
or
viewControllerB.onSelect = { [weak self]
self?.onSelection()
}
will cause the closure to not capture vcA meaning there will be no retain cycle. It is safer to use weak rather than unowned. The closure can't guarantee that un-captured objects will be around at execution weak will allow such objects to be nil, unowned is, in a manner, stipulating that it won't be nil and instructing the program to crash if it is.

Optimizing capture lists

Is there such a thing? Is there any difference between the two below? Is one more "correct" than the other?
All objects are properties of self (let's say a view controller) and have the same lifetime as self. We can introduce an object with a shorter lifetime than self, which would be weak, but the same question applies.
objectOne.doSomething { [unowned self] in
self.objectTwo.finish()
self.tableView.reloadData()
// self.someDelegate?.didFinishSomething()
}
vs
objectOne.doSomething {
[unowned objectTwo = self.objectTwo,
unowned tableView = self.tableView
// weak someDelegate = self.delegate
] in
objectTwo.finish()
tableView.reloadData()
// someDelegate?.didFinishSomething()
}
Apple has this example in their docs:
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
delegate?.doSomething()
}
In this case, delegate can have a shorter lifetime than self, but why not use it like this?
lazy var someClosure: () -> String = {
[unowned self] in
// closure body goes here
self.delegate?.doSomething()
}
Yes, there is an important difference. In the case of the Apple docs, the code alternative you presented:
lazy var someClosure: () -> String = {
[unowned self] in
// closure body goes here
self.delegate?.doSomething()
}
will look up the current delegate on self when the closure runs.
In the Apple example version:
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
delegate?.doSomething()
}
the weak delegate var in the capture list is copying the delegate pointer on self that exists at the time of closure declaration, not execution. So if the value of self.delegate changes after the closure is declared and is different at the time the closure runs, the Apple version of the closure will have a nil delegate (assumedly since the reference to the old delegate was weak) and do nothing.
So as a general rule, copying values or references in capture lists ([someIdentifier = someProperty]) is how to use the values or references as they exist at the moment the closure is defined. Whereas declaring weak or unowned self in the capture list ([weak self]) and then accessing properties on that weak reference ({ self?.someProperty }) will get the values of the properties as they exist when the closure is executed.
One of the problems that [unowned objectOne = self.objectOne] might cause is with lazy var UIViews and racing conditions: if you aren't careful and the lazy init gets called from different threads, two instances might end up being created. Furthermore, if your superview.addSubview(objectOne) call is in the lazy init, both instances will be added to the superview, and objectOne will point to one of the two instances.

ARC in Swift how to resolve strong reference cycle when assign property as function

Flowing code, I did try to create an object assign object property to a functions. And after init object I did try assign it to nil. But object did not release (because deinit never called).
I think problem is strong reference cycle between property and owner object. If really has strong reference cycle here, how to resolve this problem when assign property directly with a function?
class MyClass {
var aProperty: (() -> ())?
init() {
// problem when assign property as a method
self.aProperty = aMethod
}
func aMethod() {
print("method!!!")
}
deinit {
print("MyClass is being deinitialized")
}
}
var instance: MyClass? = MyClass()
instance?.aProperty?()
instance = nil
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. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
from
Strong Reference Cycles for Closures
In your case you should apply capture list when assigning a method to the property like this
init() {
self.aProperty = { [unowned self] in self.aMethod() }
}
You can still use a capture list to prevent the cycle. Just wrap the method call in a closure as shown in the code below.
class MyClass {
var aProperty: (() -> ())?
init() {
// use a capture list to prevent a reference cycle
self.aProperty = { [unowned self] in self.aMethod() }
}
func aMethod() {
print("method!!!")
}
deinit {
print("MyClass is being deinitialized")
}
}
var instance: MyClass? = MyClass()
instance?.aProperty?()
instance = nil
This eliminates the strong reference cycle in my testing.

Resources