Struct vs Class vs Actor
Updated:
06.Struct vs Class vs Actor
Compare with Struct and Class
To Compare them, creating the same object using a struct and a class dive into the differences
- Struct Object Code
When we create struct don’t actually have to create an init
struct StructClassActorBootCamp: View {
// MARK: - PROPERTY
// MARK: - BODY
var body: some View {
Text("Hello, World!")
.onAppear {
runTest()
}
}
}
// MARK: - PREVIEW
struct StructClassActorBootCamp_Previews: PreviewProvider {
static var previews: some View {
StructClassActorBootCamp()
}
}
struct MyStruct {
var title: String
}
// MARK: - EXTENSTION
extension StructClassActorBootCamp {
private func runTest() {
print("Test Started")
structTest1()
}
private func structTest1() {
let objectA = MyStruct(title: "Starting title!")
print("ObjectA: ", objectA.title)
var objectB = objectA
print("ObjectB: ", objectB.title)
objectB.title = "Second title!"
print("ObjectB title changed.")
print("ObjectA: ", objectA.title)
print("ObjectB: ", objectB.title)
}
}
You will notice that when you change the title of objectB did not update, only object b’s title did update because this struct is a value type and when you pass a value type. ObjectB is actually totally distinct and separate from ObjectA because it passed the values and not a reference
- Class Object Code
Class we do have to give them an actual explicit initializer
struct StructClassActorBootCamp: View {
// MARK: - PROPERTY
// MARK: - BODY
var body: some View {
Text("Hello, World!")
.onAppear {
runTest()
}
}
}
// MARK: - PREVIEW
struct StructClassActorBootCamp_Previews: PreviewProvider {
static var previews: some View {
StructClassActorBootCamp()
}
}
struct MyStruct {
var title: String
}
class MyClass {
var title: String
init(title: String) {
self.title = title
}
}
// MARK: - EXTENSTION
extension StructClassActorBootCamp {
private func runTest() {
print("Struct Test")
structTest1()
printDivider()
print("Class Test")
classTest1()
}
private func printDivider() {
print("""
-----------------------------
""")
}
private func structTest1() {
let objectA = MyStruct(title: "Starting title!")
print("ObjectA: ", objectA.title)
print("Pass the VALUES of objectA to objectB")
var objectB = objectA
print("ObjectB: ", objectB.title)
objectB.title = "Second title!"
print("ObjectB title changed.")
print("ObjectA: ", objectA.title)
print("ObjectB: ", objectB.title)
}
private func classTest1() {
let objectA = MyClass(title: "Starting Title")
print("ObjectA: ", objectA.title)
// When we are changing the title here not chaning the object itself. We are changing the title inside the object
print("Pass the REFERENCE of objectA to objectB")
let objectB = objectA
print("ObjectB: ", objectB.title)
objectB.title = "Second title!"
print("ObjectB title changed.")
print("ObjectA: ", objectA.title)
print("ObjectB: ", objectB.title)
}
}
You can notice the big difference is that both objectA and B’s title changed. The struct is passing the VALUE from objectA to objectB Otherwise, the class is passing the REFERENCE from them. Here, main point is Class objectA,B are both actually pointing to the same object in memory, so that underlying reference is connected to anywhere that is pointing to that reference so both object
https://blog.onewayfirst.com/ios/posts/2019-03-19-class-vs-struct/
-
Value type creates the copy (a new instance) and stores the data (on Stack Memory). Modifying the data (or the instance) does not affect the original.
-
Reference type creates the shared instance that points the memory location of the data (on Heap Memory). Modifying the data affects the original.
-
Mutating Struct example code
// mutating anywhere but, it is mutating it when you change the title
struct MyStruct {
var title: String
}
// Immutable struct
struct CustomStruct {
let title: String
func updateTitle(newTitle: String) -> CustomStruct {
CustomStruct(title: newTitle)
}
}
// MutatingStruct you have to add the workd mutaing and want to restuct maybe the way this struct gets updated
struct MutatingStruct {
private(set) var title: String
init(title: String) {
self.title = title
}
mutating func updaterTile(newTilte: String) {
title = newTilte
}
}
extension StructClassActorBootCamp {
private func structTest2() {
print("structTest2")
var struct1 = MyStruct(title: "Title1")
print("Struct1: ", struct1.title)
struct1.title = "Title2"
print("Struct1: ", struct1.title)
var struct2 = CustomStruct(title: "Title1")
print("Struct2: ", struct2.title)
struct2 = CustomStruct(title: "Title2")
print("Struct2: ", struct2.title)
var struct3 = CustomStruct(title: "Title1")
print("Struct3: ", struct3.title)
struct3 = struct3.updateTitle(newTitle: "Title2")
print("Struct3: ", struct3.title)
var struct4 = MutatingStruct(title: "Title1")
print("Struct4: ", struct4.title)
struct4.updaterTile(newTilte: "Title2")
print("Struct4: ", struct4.title)
}
}
- Class example code
The difference of struct and class, in the struct, it has to make this variable because when we mutated the title we were chaining the object we were literally mutating this object tha actually changed. Mutating means we are going to take the current values we’re going to change them we’re going to create a new object with new values
class MyClass {
var title: String
init(title: String) {
self.title = title
}
func updaterTile(newTilte: String) {
title = newTilte
}
}
extension StructClassActorBootCamp {
private func classTest2() {
print("classTest2")
let class1 = MyClass(title: "Title1")
print("Class1: ", class1.title)
class1.title = "Title2"
print("Class1: ", class1.title)
let class2 = MyClass(title: "Title1")
print("Class2: ", class2.title)
class2.updaterTile(newTilte: "Title2")
print("Class2: ", class2.title)
}
}
https://stackoverflow.com/questions/24217586/structure-vs-class-in-swift-language
https://medium.com/@vinayakkini/swift-basics-struct-vs-class-31b44ade28ae
https://stackoverflow.com/questions/27441456/swift-stack-and-heap-understanding
https://stackoverflow.com/questions/24232799/why-choose-struct-over-class/24232845
- What is ARC in Swift and how is it used (Automatic Reference Counting) in Object-C developers used to have to actually keep track of the count in Swift this is mostly done automatically for us. ARC is only for objects that are in the heap such as classes and actors
Value types which store on stack memory such as structures and enumerating are just copying the data to data. Therefore it is not affected by ARC
ARC is to track and manage the app’s memory usage. Arc automatically frees up the memory used by class instances when those instances are no longer needed.
Every instance of a class has a property called reference count so if reference count is greater than 0, the instance is still kept in memory otherwise, it will be removed from the memory.
- Weak and Strong reference
When you create a strong reference without using weak self, you are telling the compiler that we absolutely need self this class we need it to to still be in memory when we come back the asynchronous code if the user moves away to another screen in the app and the system tries to de-allocate this class it will then understand because there is still a reference so, Strong reference class is not going to de-allocate
What is weak self? This is making class as optional that if the class wants to de-allocate that’s totally fine because if when the code comes back the asynchronous code if the class is still in memory we’ll handle the response but if it is de-allocated for whatever reason
If you keep that strong reference you are keeping at least on reference count to that object in memory and so that object in memory being the class is never going to get de-allocated until all of the references are gone
So, if we make it a weak reference it’s then going to make this reference an optional this reference being weak will not count towards the reference count when it is trying to figure our if this instance needs to be kept in memory still.
https://medium.com/doyeona/automatic-reference-counting-in-swift-arc-weak-strong-unowned-925f802c1b99
Class vs Actor
Actors are more or less the same thing as classes except they are thread safe
https://www.backblaze.com/blog/whats-the-diff-programs-processes-and-threads/
In the Multi Thread, it might access an object in the heap and then this thread might also access the same object in the heap. Now the heap is going to synchronize them and that’s basically because there is noting by default that’s going to stop two different threads from accessing the same object in memory.
Now both classes and actors are stored in the heap cause they are more or less the same thing the only big difference is that the actors are going to be thread safe
In the class if two different threads are accessing that class they can function at the exact same time. But in an actor if two threads are accessing the same actor one of them is probably going to have to await that other thread to finish its processes before the second thread can get into that actor
Actors require to be in an asynchronous environment and when you want to access anything inside the actor you need to await on it. If they are accessing at the same time you might run into a situation where one thread might have to literally await for the other thread to finish
actor MyActor {
var title: String
init(title: String) {
self.title = title
}
func updaterTile(newTilte: String) {
title = newTilte
}
}
private func actorTest1() {
Task {
let objectA = MyActor(title: "Starting Title")
await print("ObjectA: ", objectA.title)
print("Pass the REFERENCE of objectA to objectB")
let objectB = objectA
await print("ObjectB: ", objectB.title)
// actor is now thread safe so we can't chnage the value from outside the actor itself
// objectB.title = "Second title!"
await objectB.updaterTile(newTilte: "Second title!")
print("ObjectB title changed.")
await print("ObjectA: ", objectA.title)
await print("ObjectB: ", objectB.title)
}
}
Warp up
- Value vs Reference type
-
Value Types : Struct, Enum, String Int, etc
-
Stored in the stack
-
Faster
-
Thread safe
-
When you assign or pass value type a new copy of data is created.
-
-
Reference Types: Class, Function, Actor
-
Stored in the Heap
-
Slower, but synchronized
-
Not thread safe (by default)
-
When you assign or pass reference type a new reference to original instance will be created (pointer)
-
- Stack vs Heap
-
Stack
-
Stored value types
-
Variables allocated on the stack are stored directly to the memory, and access to this memory is very fast
-
Each thread has it’s own stack
-
-
Heap
-
Stored reference types
-
Shared across threads
-
- Struct vs Class vs Actor
-
Struct
-
Based on Values
-
Can be mutated
-
Stored in the Stack!
-
-
Class
-
Based on Reference (Instance)
-
Stored in the Heap!
-
Inherit from other classes
-
-
Actor
- Same as Class, but thread safe!
Structs: Data Models, View Classes: ViewModels Actors: Shared ‘Manager’ and ‘Data Store’
🗃 Reference
SwiftUI Thinking - https://youtu.be/-JLenSTKEcA
Leave a comment