[Swift Concurrency] Struct vs Class vs Actor

Woozoo·2023년 5월 3일
1

[Swift Concurrency]

목록 보기
9/13

제일 중요함!!

struct StructClassActorBootcamp: View {
    var body: some View {
        Text("Hello, World!")
            .onAppear {
                runTest()
            }
    }
}

struct MyStruct {
    let title: String
}

extension StructClassActorBootcamp {
    private func runTest() {
        print("Test started!")
    }
    
    private func structTest1() {
        let objectA = MyStruct(title: "Starting title!")
        print("ObjectA: ", objectA.title)
        let objectB = objectA
        print("ObjectB:", objectB.title)
        
        objectB.title = "Second title!"
    }
}

요렇게 구성이 되어 있다고 생각해봅시다

objectA 를 그대로 objectB에 넣어주고,
ojbectB의 title을 변경하려하면 에러가 발생하죠

MyStruct에 있는 프로퍼티를 let으로 선언했기 때문에
var로 바꿔줘야한다~고 이야기합니다


두번째 에러는 let 으로 선언했던 objectB를 var로 바꿔야한다고 이야기하고 있고!



이번엔 클래스로 해볼까요?


바로 전에 했던 struct처럼 에러가 뜨죠?

근데 fix를 누르면 struct를 할 때처럼 objectB를 변수로 바꾸는 게 아니라, class내의 프로퍼티 자체를 var로 바꿔주고
메소드 내의 objectB는 let으로 선언되도 에러가 안 뜨는 걸 볼 수 있음

class는 참조타입이고 그 객체의 주소값을 바라보고 있어서 그렇겠죠!


그럼 값타입과 참조타입을 어떤 때에 사용해야하는가?

참조 타입은 매번 싱크를 맞춰주게 되니까
값타입에 비해서는 메모리를 더 먹음

어떻게 메모리에 저장되는지는 조금 이따 알아볼거고
먼저 struct와 class 업데이트하는 걸 해봅시다


structTest2()를 만들었는데 struct1을 let으로 선언하면 에러가 뜸
var로 바꿔주고


struct2 처럼 업데이트 하면 값타입의 struct를 매번 만들어줘야하잖음

아~
이렇게 해줬던 게 비기너들이 첨에 이해하기 쉬워서 그런거였구나


struct의 프로퍼티를 struct내에서 바꿔주려고 하면
첫번째로 title 바꿔주라는 에러가 뜨고


function에 mutating 키워드를 붙이라고함

이렇게 하면 title을 바꾸게 되면 오브젝트 자체가 변경되게 됨


struct4를 이제 그냥 update해줄 수 있게됐죠!

struct MyStruct {
    var title: String
}

/// Immutable struct
struct CustomStruct {
    let title: String
    
    func updateTitle(newTitle: String) -> CustomStruct {
        CustomStruct(title: newTitle)
    }
}

struct MutatingStruct {
    var title: String
    
    mutating func updateTitle(newTitle: String) {
        title = newTitle
    }
}

이렇게 3개의 차이가 뭔지 알겠나?!

struct 를 업데이트하는 것에 제한을 주는 것을 좋아하면 Mutating 쓰셈

첫번째처럼 그냥 쓰는 것도 솔직히 상관은 없음
코드 깔끔하잖음


Mutating 쓸 때 private으로 해주고 (set)만 가능하게 하는거임

내부에서는 수정이 가능하지만 외부에서는 수정이 불가능하게!!

struct내의 프로퍼티를 var 로 선언할지 let으로 선언할지는
의견들이 다양한가봄 찾아보자


제일 처음 배울 때 헷갈렸던 거


class 같은 경우에 let으로 선언됐는데도 안의 프로퍼티들을 바꾸는게 가능함..!

class MyClass {
    var title: String
    
    init(title: String) {
        self.title = title
    }
    
    func updateTitle(newTitle: String) {
        title = newTitle
    }
}

이번에도 update메소드를 만들어봅시다

class엔 mutating을 붙일 수 없다고 하죠
그냥 update가 가능합니다

Value와 Reference의 차이

기본적인 값 타입들은 stack에 저장된다!


값타입을 복사해서 넘기는 게 여러개의 레퍼런스를 하는 것보다
효율적임!

싱크를 일일이 안 맞춰줘도 되니까!

iOS어플 개발하다보면 여러개의 Thread를 관리하게 되는 걸 볼 수 있음


ARC

Automatic Reference Counting

Value 타입은 ARC의 영향을 받지 않음


Actor

Class vs Actor

클래스랑 비슷하면서 다름

Actor는 Thread safe 함

예를들어서 멀티스레드 환경에서
힙에 저장된 어떤 오브젝트를 여러개의 스레드에서 참조하고 있었다고 해보셈

그럼 만약에 이 태스크들이 동시에 요청이 되면
멘붕하잖음
다 같은 애를 바라보고 있으니까

그럴 때 Actor라는 키워드를 붙이면 await를 해주게 됨
코드로 확인해봅시다

MyActor라는 actor를 만들어줬다

그리고 메소드에서 이 actor 인스턴스를 만들어주면
에러가 뜨는 걸 볼 수 있음
메소드에 async를 붙이거나

Task { } 안에 넣어줘야합니다

Task 안에 넣어주고 나면 객체에 접근하려고 할 때마다
await키워드를 붙여줘야함!


await 다붙여주고나면 외부에서 actor를 수정할 수 없다는 에러가 나옴!

그럼 내부에서 해주면 되겠죠?

-> struct에서 update해주던 느낌으로!!

private func actorTest1() {
    Task {
        print("classTest1")
        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)
        
        await objectB.updateTitle(newTitle: "Second title! ")
        print("ObjectB title changed.")
        
        await print("ObjectA: ", objectA.title)
        await print("ObjectB: ", objectB.title)
    }
}

이렇게!!

actor도 class도 참조
힙에 저장됨

actor는 참조 형태가 비동기로 실행될 경우에 order를 잘 해줘서
thread safe하게 작업을 실행해주는 느낌


View init할 때랑
ViewModel init할 때 print 찍어보면

view는 매번 프린트가 찍히는데
ViewModel은 젤 처음 init될 때만 찍힘

 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
 
 REFERENCY 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:
 - Stores 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:
 - Stores Reference types
 - Shared across threads!
 
 ____________________
 
 STRUCT:
 - Based on VALUES
 - Can be mutated
 - Stored in the Stack!
 
 CLASS:
 - Based on REFERENCES (INSTANCES)
 - Stored in the Heap!
 - Inherit from other classes         // It's common interview question but in swiftUI Do we really use this?
 
 ACTOR:
 - Same as Class, but thread safe!
 
 _____________________
 
 Structs: Data Models, Views
 Classes: ViewModels
 Actors: Shared 'Manager' and 'Data store'

profile
우주형

0개의 댓글