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!
@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.
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
Multidimensional Dictionaries Possible in Swift
Mpmusicplayercontroller Setqueuewithstoreids Playing Index
Conform to Protocol and Keep Property Private
Swift Conditional Conformances with Generic Type
Require Associatedtype to Be Representable in a @Convention(C) Block
Gcd Pattern for Chaining Async Operations While Piping the Results
Strange Behaviour for Recursive Enum in Swift (Beta 7)
How to Make Sfspeechrecognizer Available on MACos
Realitykit - Load Another Scene from the Same Reality Composer Project
How to Get Distinct Results from a Single Field in Core Data (Swift 4)
Combining Watchconnectivity and Complications
Drawing a Gradient Color in an Arc with a Rounded Edge
Firebase Retrieve Image from Url Save with Firebase Database
iOS Charts Library - How to Handle X-Axis Duplicate Values