I created a project to test [weak self]
There is:
MenuView
, that is home view, and does only navigation to the ContentView
,ContentView
, that indicates the result of resource-intensive calculation,ViewModel
, which has a resource-intensive calculation (100 mln loop appending to the array)So, in the code I use weak self, that makes ViewModel
deinitialized, when user closes the ContentView
. But if I open CPU graphs, there still are calculations, making to the end, even if this instance was deinitialized:
If you compare this graphs with use of [weak self]
and without it - there is absolutaly no difference.
Here is the whole code:
struct ContentView: View {
@StateObject var vm = ViewModel()
var body: some View {
VStack {
Text("App started")
Text(vm.helloData.count.description)
}
.padding()
}
}
class ViewModel: ObservableObject {
@Published var helloData: [String] = []
init() {
print("ViewModel инициализирован")
let count = UserDefaults.standard.integer(forKey: "count")
UserDefaults.standard.set(count+1, forKey: "count")
var data: [String] = []
DispatchQueue.global().async { [weak self] in
for _ in (0..<100_000_000) {
data.append("Hello, world!")
}
DispatchQueue.main.sync {
self?.helloData = data
}
}
}
deinit {
print("ViewModel деинициализирован")
let count = UserDefaults.standard.integer(forKey: "count")
UserDefaults.standard.set(count-1, forKey: "count")
}
}
struct ContentView: View {
@StateObject var vm = ViewModel()
var body: some View {
VStack {
Text("App started")
Text(vm.helloData.count.description)
}
.padding()
}
}
struct MenuView: View {
@AppStorage ("count") var count: Int = 0
init() {
self.count = 0
}
var body: some View {
NavigationStack{
NavigationLink("go to ContentView", destination: ContentView())
}
.overlay {
VStack {
Text(count.description)
.font(.title)
Spacer()
}
}
}
}
Where am i wrong? As i understand, the reason of using weak self is to get rid of this useless calculations...
As i understand, the reason of using weak self is to get rid of this useless calculations
No it is't. The point of using weak self
is to break potential reference cycles. In your example, I don't think it's a problem anyway. The closure maintains a reference to self
because of the assignment to self.helloData
but there's no reference from self
to the closure that I can see: the only thing that holds a reference to the closure will be the dispatch queue - or the task on it.
I assume by "useless calculations" you mean the appends to data
inside the for
loop, but data
is a local variable, not a property: there's no reason why weak self
would affect it. You can explicitly exit the loop if self
does go away with a guard
inside the loop e.g.
guard self != nil else { return } // or break
Incidentally, weak references impose a slight performance penalty because of the way they are implemented. IIRC when the instance is deallocated, all weak references have to be set to nil
and I think it is done via an extra level of indirection, somehow.
Edit: the implementation is slightly more complex than I thought
https://www.mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html