Two Different threads가 are accessing same Object할 때 문제가 생김
class MyDataManager {
static let instance = MyDataManager()
private init() { }
var data: [String] = []
func getRandomData() -> String? {
self.data.append(UUID().uuidString)
print(Thread.current)
return data.randomElement()
}
}
struct HomeView: View {
let manager = MyDataManager.instance
@State private var text: String = ""
let timer = Timer.publish(every: 0.1, tolerance: nil, on: .main, in: .common, options: nil).autoconnect()
var body: some View {
ZStack {
Color.gray.opacity(0.8).ignoresSafeArea()
Text(text)
.font(.headline)
}
.onReceive(timer) { _ in
if let data = manager.getRandomData() {
self.text = data
}
}
}
}
홈뷰를 만들고 DataManager에서 0.1초마다 getRandomData가 호출되게 해줌
0.1초마다 uuid가 나오는 걸 볼 수 있는데
다른 탭인 BrowseView에서도 같은 로직을 이번엔 0.01초마다 가져오게 해봅시다
지금은 문제가 없음!
Main Thread에서 홈뷰도 브라우즈뷰도 동작하고 있어서
백그라운드로 태스크를 옮겨봅시다
홈뷰랑 BrowseView둘다 이렇게 만들어줌
이렇게 됐을 때 potential Thread 이슈가 생길 수 있겠죠
두 태스크가 지금 같은 객체를 바라보고 있으니까!
이거 Xcode에서 Thread sanitizer
Edit scheme에 추가해줄 수 있음
그리고 다시 실행해보면 이런 에러가 뜨는 걸 볼 수 있습니다!
이전에는 이렇게 completionHandler로 이런 문제들을 해결해줬었음
actor MyActorDataManager {
static let instance = MyActorDataManager()
private init() { }
var data: [String] = []
func getRandomData() -> String? {
self.data.append(UUID().uuidString)
print(Thread.current)
return self.data.randomElement()
}
}
이번엔 actor로 해결을 해봅시다
Task안에 로직을 추가해주면!
됩니다~
한 가지 알아둬야할 건 이제 actor로 선언한 manager의 경우 프로퍼티를 가지고 올 때도 async라고 뜨는 걸 볼 수 있음
그래서 actor의 프로퍼티를 가져올 때도 Task {}안에 감싸줘야한다는거?
만약에 async로 처리가 필요 없으면?
await이 필요없는데도 Task를 지금 감싸줘야하잖음
이럴 때 쓰는 키워드가
nonisolated 입니다~
캬~
프로퍼티도 가능함!!
또 알아둬야할 건 isolated로 선언된 건 actor내의 다른 async친구들을 가져올 수 가 없음