Swift Memory Management: Storing Func in Var

Swift assign function to var cause retain cycle?

In Swift, all captured variables are captured by reference (in Apple Blocks terminology, all captured local variables are __block). So the t inside the block is shared with the t outside the block; the block does not hold an independent copy of t.

Originally, there is a retain cycle in the second case too, as the block holds a reference to this shared copy of t, and t points to the first Test object, and that Test object's block property points to the block. However, when you re-assign the shared variable t (which is visible both inside and outside the block), you break the retain cycle, because t no longer points to the first Test object.


In the first case, the t is effectively captured by value, because the t is evaluated immediately in the expression t.returnInt rather than be captured as a variable in a block. So a reassignment of t outside the block later has no effect on the block, and does not break the retain cycle. So you can think of

t.block = t.returnInt 

as kind of like

let tmp = t
t.block = {
return tmp.returnInt()
}

Swift issue when storing Object inside a struct

First, you should be very, very careful about putting reference types inside of value types, and especially a mutable reference type that is visible to the outside world. Structs are always value types, but you also want them to have value semantics, and it's challenging to do that while containing reference types. (It's very possible, lots of stdlib types do it in order to implement copy-on-write; it's just challenging.)

So the short version is "you almost certainly don't want to do what you're doing here."

But if you have maintained value semantics in SomeStruct, then the answer is to just make a copy. It's always fine to make a copy of a value type.

someStruct.closure = { [someStruct] in
print(someStruct)
}

This gives the closure it's own immutable value that is a copy of someStruct. Future changes to someStruct won't impact this closure.

If you mean for future changes to someStruct to impact this closure, then you may be violating value semantics, and you should redesign (likely by making SomeStruct a class, if you mean it to have reference semantics).

Swift Memory management in the class

As your class A has no stored properties there can be no retain cycle.

Calling a function (closure) does not create retain cycles. Storing the closure might.

Storing Variables to Memory Swift

i first declared the variable as:

var startCharge : Double = 0
var lastCharge : Double = UserDefaults.standard.double(forKey: "lastCharge")

and then in the function just set the variables in the function

startCharge = batteryLevel
endCharge = batteryLevel

UserDefaults.standard.set(lastCharge, forKey: "lastCharge")

Memory management: retain cycle with weak var, non retain cycle with unowned. Why?

I suppose problem is that you run it in Playground. Try to run it in real app and you will see that B is deallocated

struct A {
weak var b: B?

init(b: B) {
self.b = b
}

func setup() {
print("A setup")

b?.didSomethingClosure = {
print("A: b did do something")
self.printSomething()
}
}

func printSomething() {
print("A: A did do something")
}
}

class B {

var didSomethingClosure:(() -> Void)?

func doSomething() {
print("B: do something")
didDoSomething()
}

func didDoSomething() {
print("B: did something")
if let closure = didSomethingClosure {
closure()
}
}

deinit {
print("B: deinit")
}
}

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let b = B()
let a = A(b: b)

a.setup()
b.doSomething()

print("end do") // B is deallocated here
}
}

Swift closure stored as a variable produces memory leak

If you don't need to use self, then you can use the cell itself, and modifying your closure implementation in cell like this

public class MyCell: UICollectionViewCell {
public var callback: (()->Void)?
}

then you can use it, like this example

class CustomController: UIViewController {
private func onActionOccurs(_ cell: MyCell) {
cell.backgroundColor = .red // For example
}

// After dequeuing the cell:
{
cell.callback = {
cell.backgroundColor = .red // For example
}
}
}

but if you need to use a ViewController method then you need to use the [weak self] capture list
If you need use UIViewController methods

class CustomController: UIViewController {
private func onActionOccurs(_ cell: MyCell) {
cell.backgroundColor = .red // For example
}

// After dequeuing the cell:
{
cell.callback = { [weak self] in
guard let self = self else { return }

self.viewControllerMethod()
}
}
}

@escaping completionHandler - How does memory management work for them?

I finally figured out!

  1. @escaping closure gets call even after class deinitialized, But won't it will get nil instance variable if you properly managed memory by using weak self.

  2. If we don't call @escaping closure at all it doesn't occupy any memory.

Sample Code

final class DataSource {

typealias RequestCompleted = (_ data:String?, _ error: NSError?) -> Void

// MARK: Shared Instance
static let sharedInstance = DataSource()

// MARK: Concurrent queue for thread-safe array

fileprivate let concurrentQ = DispatchQueue(label: "com.test.concurrentQueue",
qos: .userInitiated,
attributes: .concurrent)

// MARK:- Local Variable
fileprivate var _dataHandler: RequestCompleted?
fileprivate var dataHandler: RequestCompleted? {
get {
return concurrentQ.sync {
return _dataHandler
}
}

set {
concurrentQ.async(flags: .barrier){ [weak self] in
self?._dataHandler = newValue
}
}
}

fileprivate var result:(data: String?, error: NSError?) {
didSet {
if let handlr = dataHandler {
handlr(result.data, result.error)
self.isRequestInProgress = false
}
}
}

fileprivate var _isRequestInProgress = false
fileprivate var isRequestInProgress: Bool {
get {
return concurrentQ.sync {
return _isRequestInProgress
}
}

set {
concurrentQ.async(flags: .barrier){ [weak self] in
self?._isRequestInProgress = newValue
}
}
}

// MARK:- Private init()
private init() {

}

deinit {
print("Deinitialized")
}

internal func fetchData(_ onCompletion: @escaping RequestCompleted) {
self.dataHandler = onCompletion
if self.isRequestInProgress == true { print("TTT: In Progress")
return
} else {

self.isRequestInProgress = true

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(20)) {
// Code
self.result = ("Done", nil)
}
}
}
}

ViewController

class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
print("ViewController 1 Function")

DataSource.sharedInstance.fetchData { (name, error) in
print("ViewController 1 Handler")
}
}
}

ViewController2

class ViewController2: UIViewController {
var str = "Test"
var arr = [1, 2, 3]

override func viewWillAppear(_ animated: Bool) {
print("ViewController 2 Function")

DataSource.sharedInstance.fetchData { [weak self] (name, error) in
print("ViewController 2 Handler")
print("CCCC\(self?.arr ?? [0])")
print("SSSS\(self?.str ?? "Happy")")
}
}

deinit {
print("VC2.. deinit")
}
}

ViewController3

class ViewController3: UIViewController {
override func viewWillAppear(_ animated: Bool) {
print("ViewController 3 Function")

DataSource.sharedInstance.fetchData { (name, error) in
print("ViewController 3 Handler")
}
}

deinit {
print("VC3.. deinit")
}

}

And For Low memory warning-

Since in swift

collection types and tuple are value type,

I will either remove person objects or set tuple to nil in case of low memory warning. It won't impact data on my Controller view.



Related Topics



Leave a reply



Submit