Skip to content

Instantly share code, notes, and snippets.

@yimajo
Last active November 13, 2021 12:04
Show Gist options
  • Save yimajo/e935178baf7bf1e48a98195b225a5a93 to your computer and use it in GitHub Desktop.
Save yimajo/e935178baf7bf1e48a98195b225a5a93 to your computer and use it in GitHub Desktop.
DAOでアクター使ってくとき、actor型をそのまま使うのかそれとも(at)globalActorを使うのかのメモ
import _Concurrency
/*
DAOでアクター使って行きたいとき(モチベーション的には非同期処理であることを矯正したい場合)、
設計としては
actor型をDAOの型とするパターンと、globalActorをDAOで利用する場合の2パターンある。
SampleAがactorを使うパターンで、SampleBがglobalActorを使うパターン。
*/
enum SampleA {
private actor NumbersDataStore {
var savedValues: [Int] = []
func save(_ number: Int) async {
savedValues.append(number)
}
func numbers() -> [Int] {
savedValues
}
}
static func execute() async {
let dataStore = NumbersDataStore()
await dataStore.save(1)
print(await dataStore.numbers())
await dataStore.save(2)
print(await dataStore.numbers())
}
}
enum SampleB {
@globalActor
private struct DBActor {
actor MyActor {
// DBActor.MyActorというのを作り、これが勝手に使われる
}
// protocolにしたがってsharedを定義
static var shared = MyActor()
}
private class NumberDataSource {
@DBActor var savedValues: [Int] = []
@DBActor
func save(_ number: Int) {
savedValues.append(number)
}
@DBActor
func numbers() -> [Int] {
savedValues
}
// これはコンパイルエラー
// func count() -> Int {
// savedValues.count
// }
}
private class NumberDataSource2 {
@DBActor var savedValues: [Int] = []
@DBActor
func save(_ number: Int) {
savedValues.append(number)
}
@DBActor
func numbers() -> [Int] {
savedValues
}
// これはコンパイルエラー
// func count() -> Int {
// savedValues.count
// }
}
static func execute() async {
let dataSource = NumberDataSource()
let dataSource2 = NumberDataSource2()
await dataSource.save(1)
print(await dataSource.numbers())
await dataSource.save(2)
print(await dataSource.numbers())
await dataSource2.save(3)
print(await dataSource2.numbers())
}
}
enum SampleC {
@globalActor
private struct DBActor {
actor MyActor {
// DBActor.MyActorというのを作り、これが勝手に使われる
}
// protocolにしたがってsharedを定義
static var shared = MyActor()
}
private class DataStore {
var savedValues: [Int] = []
init(savedValues: [Int]) {
self.savedValues = savedValues
}
}
private class NumberDataSource {
@DBActor var dataStore: DataStore
init(dataStore: DataStore) {
self.dataStore = dataStore
}
@DBActor
func save(_ number: Int) {
dataStore.savedValues.append(number)
}
@DBActor
func numbers() -> [Int] {
dataStore.savedValues
}
// これはコンパイルエラー
// func count() -> Int {
// savedValues.count
// }
}
private class NumberDataSource2 {
@DBActor var dataStore: DataStore
init(dataStore: DataStore) {
self.dataStore = dataStore
}
@DBActor
func save(_ number: Int) {
dataStore.savedValues.append(number)
}
@DBActor
func numbers() -> [Int] {
dataStore.savedValues
}
// これはコンパイルエラー
// func count() -> Int {
// dataStore.savedValues.count
// }
}
static func execute() async {
let dataStore = DataStore(savedValues: [])
let dataSource = NumberDataSource(dataStore: dataStore)
let dataSource2 = NumberDataSource2(dataStore: dataStore)
await dataSource.save(1)
print(await dataSource.numbers())
await dataSource.save(2)
print(await dataSource.numbers())
await dataSource2.save(3)
print(await dataSource2.numbers())
}
}
enum SampleD {
@globalActor
private struct DBActor {
actor MyActor {
// DBActor.MyActorというのを作り、これが勝手に使われる
}
// protocolにしたがってsharedを定義
static var shared = MyActor()
}
private actor DataStore {
var savedValues: [Int] = []
init(savedValues: [Int]) {
self.savedValues = savedValues
}
func append(_ number: Int) {
savedValues.append(number)
}
}
private class NumberDataSource {
private var dataStore: DataStore
init(dataStore: DataStore) {
self.dataStore = dataStore
}
func save(_ number: Int) async {
// dataStore.savedValues.append(number)はできない
await dataStore.append(number)
}
func numbers() async -> [Int] {
// これはできるんだ...
await dataStore.savedValues
}
}
private class NumberDataSource2 {
private var dataStore: DataStore
init(dataStore: DataStore) {
self.dataStore = dataStore
}
func save(_ number: Int) async {
await dataStore.append(number)
}
func numbers() async -> [Int] {
await dataStore.savedValues
}
}
static func execute() async {
let dataStore = DataStore(savedValues: [])
let dataSource = NumberDataSource(dataStore: dataStore)
let dataSource2 = NumberDataSource2(dataStore: dataStore)
await dataSource.save(1)
print(await dataSource.numbers())
await dataSource.save(2)
print(await dataSource.numbers())
await dataSource2.save(3)
print(await dataSource2.numbers())
}
}
// NOTE: 一つのTask { } にA,B,C,Dを囲むとAの実行後に正常動作しなかったのでTaskの階層変えてる
Task {
print("A")
await SampleA.execute()
Task {
print("B")
await SampleB.execute()
print("C")
await SampleC.execute()
print("D")
await SampleD.execute()
}
}
@yimajo
Copy link
Author

yimajo commented Nov 13, 2021

触った感じとしては

  • SampleBのように(at)globalActorを使っていく場合はDAOが複数あってそれぞれ同じスレッドで動いてほしいということはやりやすい
    • SampleB自体に意味はないけども
  • SampleCのように守りたいデータストアが別にあって複数DAOで共有したい場合にも使えるそう
    • じゃあこれSampleCのデータストア自体が間違って使われたら嫌じゃない?ということでSampleDを作ってみる
  • SampleDのように本当に守りたいデータストアをactorにして、DAO的なDataSourceたちはclassで良いとなる

まとめ

  • 守りたいグローバルな値があればそれはactorとして型にして値はプロパティにしとけばいい
    • Core Data使うときグローバルな何かがあるわけじゃないからglobalActorでいいのかもしれない

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment