Upgrade to Pro — share decks privately, control downloads, hide ads and more …

(iOSDC Japan 2023) ActorでCoreDataをスレッドから解放しよう

Himeshi
September 05, 2023

(iOSDC Japan 2023) ActorでCoreDataをスレッドから解放しよう

このスライドは、iOSDC Japan 2023 でトークを行ったものです。
https://fortee.jp/iosdc-japan-2023/proposal/240c16ac-498a-4d17-a43a-f34f0fdbe041

Himeshi

September 05, 2023
Tweet

More Decks by Himeshi

Other Decks in Programming

Transcript

  1. ͻΊ͠ʢ!IJNFTIJ@UFDIʣ
    "DUPSͰ$PSF%BUBΛ
    εϨου͔Βղ์͠Α͏
    !J04%$+BQBO

    View Slide

  2. $PSF%BUB࢖͍ͬͯ·͔͢ʁ
    2

    View Slide

  3. $PSF%BUB
    ҆શʹ࢖͍ͬͯ·͔͢ʁ
    3

    View Slide

  4. ຊ೔ͷΞδΣϯμ
    w 4XJGU$PODVSSFODZͷ΋ͱͰ$PSF%BUBΛΑΓ҆શʹ࢖͑ΔΑ͏
    ʹ͢ΔͨΊͷख๏Λߟ͑Δɻ
    4

    View Slide

  5. ຊ೔ͷΞδΣϯμ
    w 4XJGU$PODVSSFODZͷ΋ͱͰ$PSF%BUBΛΑΓ҆શʹ࢖͑ΔΑ͏
    ʹ͢ΔͨΊͷख๏Λߟ͑Δɻ
    w $PSF%BUBͷεϨουҧ൓ͱ͸ͳʹ͔
    5

    View Slide

  6. ຊ೔ͷΞδΣϯμ
    w 4XJGU$PODVSSFODZͷ΋ͱͰ$PSF%BUBΛΑΓ҆શʹ࢖͑ΔΑ͏
    ʹ͢ΔͨΊͷख๏Λߟ͑Δɻ
    w $PSF%BUBͷεϨουҧ൓ͱ͸ͳʹ͔
    w εϨουҧ൓ΛίϯύΠϧ࣌ʹνΣοΫ͢Δख๏
    6

    View Slide

  7. ͜ͷτʔΫͷର৅
    w $PSF%BUBͱ4XJGU$PODVSSFODZΛগ͠Ͱ΋৮ͬͨ͜ͱ͕͋Δਓ
    w ඇಉظॲཧ΍σʔλڝ߹ʹۤ͠Μͩ͜ͱ͕͋Δਓ
    7

    View Slide

  8. $PSF%BUBͷ
    εϨουҧ൓ͱ͸ͳʹ͔ʁ
    8

    View Slide

  9. ͜ͷίʔυ͸҆શͳ࣮૷͔ʁ
    9
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }

    View Slide

  10. 10
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    .BOBHFEPCKFDUDPOUFYUΛൃߦ
    .BOBHFEPCKFDUΛੜ੒ɺอଘ
    ͜ͷίʔυ͸҆શͳ࣮૷͔ʁ

    View Slide

  11. ͜ͷ࣮૷͸εϨουηʔϑͰ͸ͳ͍
    11
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    initͱҟͳΔεϨου͔Β
    ΞΫηεͯ͠͸͍͚ͳ͍

    View Slide

  12. ͜ͷ࣮૷͸εϨουηʔϑͰ͸ͳ͍
    12
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    /4.BOBHFE0CKFDUͱ/4.BOBHFE0CKFDU$POUFYU͸
    εϨουηʔϑͰ͸ͳ͍ɻ

    View Slide

  13. ͜ͷ࣮૷͸εϨουηʔϑͰ͸ͳ͍
    13
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    εϨουηʔϑͰ͸ͳ͍ΦϒδΣΫτΛ
    ϓϩύςΟͱͯ͠อ͍࣋ͯ͠Δɻ
    ˠҟͳΔεϨου͔ΒΞΫηε͢ΔͱεϨουҧ൓
    /4.BOBHFE0CKFDUͱ/4.BOBHFE0CKFDU$POUFYU͸
    εϨουηʔϑͰ͸ͳ͍ɻ

    View Slide

  14. $PSF%BUBͷεϨουҧ൓ͱ͸
    w /4.BOBHFE0CKFDUɺ/4.BOBHFE0CKFDU$POUFYU͸
    ͦΕΛੜ੒ͨ͠εϨουͷ֎͔ΒΞΫηε͢ΔͱεϨουҧ൓ɻ
    w σʔλͷෆ੔߹΍ΫϥογϡΛҾ͖ى͜͢ɻ
    14

    View Slide

  15. $PSF%BUBͷεϨουҧ൓ͱ͸
    w /4.BOBHFE0CKFDUɺ/4.BOBHFE0CKFDU$POUFYU͸
    ͦΕΛੜ੒ͨ͠εϨουͷ֎͔ΒΞΫηε͢ΔͱεϨουҧ൓ɻ
    w σʔλͷෆ੔߹΍ΫϥογϡΛҾ͖ى͜͢ɻ
    w ͜ΕΒͷΦϒδΣΫτΛੜ੒ͨ͠εϨουͷ֎ʹ࣋ͪग़͞ͳ͍͜ͱ
    Λ"QQMFͷυΩϡϝϯτ͸ٻΊ͍ͯΔɻ
    15
    "QQMF%FWFMPQFS%PDVNFOUBUJPOʰ/4.BOBHFE0CKFDU$POUFYUʱΑΓ

    View Slide

  16. "DUPSʹ͢Ε͹େৎ෉ʁ
    16
    actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }

    View Slide

  17. 17
    w "DUPSʹ͢Ε͹σʔλڝ߹͠ͳ͍͸ͣʜ
    actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    "DUPSʹ͢Ε͹େৎ෉ʁ

    View Slide

  18. 18
    w "DUPSʹ͢Ε͹σʔλڝ߹͠ͳ͍͸ͣʜ
    actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    ͜ͷ࣮૷͸$PODVSSFODZXBSOJOHΛग़͞ͳ͍ɻ
    "DUPSʹ͢Ε͹େৎ෉ʁ

    View Slide

  19. actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    19
    w "DUPSCPVOEBSZ֎͔Β͜ͷBDUPSΛ࢖ͬͯΈΔɻ
    func testCallCoreDataController() async throws {
    let controller = CoreDataController(
    container: container
    )
    try await controller.configure()
    }
    "DUPSʹ͢Ε͹େৎ෉ʁ

    View Slide

  20. actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    20
    w ͜ͷ࣮૷͸εϨουҧ൓ΛҾ͖ى͜͢ɻ
    ͜͜ͰΫϥογϡ
    "DUPSʹ͢Ε͹େৎ෉ʁ

    View Slide

  21. actor CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }
    "DUPS͸εϨουಉҰੑΛอূ͠ͳ͍
    21
    w JOJUͱDPOpHVSF͕ݺ͹ΕΔεϨου͸ɺ࣮ࡍʹ͸ҟͳΔɻ
    func testCallCoreDataController() async throws {
    let controller = CoreDataController(
    container: container
    )
    try await controller.configure()
    }

    View Slide

  22. $PSF%BUBͷεϨουҧ൓ͱ͸
    w /4.BOBHFE0CKFDUɺ/4.BOBHFE0CKFDU$POUFYU͸
    ͦΕΛੜ੒ͨ͠εϨουͷ֎͔ΒΞΫηε͢ΔͱεϨουҧ൓ɻ
    w σʔλͷෆ੔߹΍ΫϥογϡΛҾ͖ى͜͢ɻ
    w ͜ΕΒͷΦϒδΣΫτΛੜ੒ͨ͠εϨουͷ֎ʹ࣋ͪग़͞ͳ͍͜ͱ
    Λ"QQMFͷυΩϡϝϯτ͸ٻΊ͍ͯΔɻ
    w "DUPS͸εϨουͷಉҰੑΛอূ͠ͳ͍ɻ
    w .BOBHFE0CKFDU౳͕ੜ੒εϨουͷ֎ʹ࣋ͪग़͞Εͯ͠·͏ɻ
    22

    View Slide

  23. εϨουҧ൓Λ๷͙ैདྷख๏
    23
    w /4.BOBHFE0CKFDUͷ͔ΘΓʹ/4.BOBHFE0CKFDU*%Λ
    ϓϩύςΟͱͯ͠อ࣋͢Δɻ

    View Slide

  24. class CoreDataController {
    private var entityID: NSManagedObjectID? = nil
    private let container: NSPersistentContainer
    func update(_ newName: String) async throws {
    guard let entityID else { return }
    let moc = container.newBackgroundContext()
    try await moc.perform {
    let entity = try? moc.existingObject(with: entityID)
    as? CoreDataEntity
    entity?.name = newName
    try moc.save()
    }
    }
    }
    /4.BOBHFE0CKFDU*%Λ࢖͏
    24

    View Slide

  25. class CoreDataController {
    private var entityID: NSManagedObjectID? = nil
    private let container: NSPersistentContainer
    func update(_ newName: String) async throws {
    guard let entityID else { return }
    let moc = container.newBackgroundContext()
    try await moc.perform {
    let entity = try? moc.existingObject(with: entityID)
    as? CoreDataEntity
    entity?.name = newName
    try moc.save()
    }
    }
    }
    /4.BOBHFE0CKFDU*%Λ࢖͏
    25
    ࣮ࡍͷNBOBHFEPCKFDUͷܕͷ͔ΘΓʹ
    /4.BOBHFE0CKFDU*%Λ࢖༻
    .BOBHFEPCKFDUʹΞΫηε͢Δʹ͸GFUDI͢Δ

    View Slide

  26. class CoreDataController {
    private var entityID: NSManagedObjectID? = nil
    private let container: NSPersistentContainer
    func update(_ newName: String) async throws {
    guard let entityID else { return }
    let moc = container.newBackgroundContext()
    try await moc.perform {
    let entity = try? moc.existingObject(with: entityID)
    as? CoreDataEntity
    entity?.name = newName
    try moc.save()
    }
    }
    }
    /4.BOBHFE0CKFDU*%Λ࢖͏
    26
    .BOBHFE0CKFDUʹΞΫηε͢Δʹ͸
    ຖճGFUDI͠ͳ͚Ε͹ͳΒͳ͍
    .BOBHFE0CKFDUͷܕ৘ใ͕ͳ͍

    View Slide

  27. εϨουҧ൓Λ๷͙ैདྷख๏
    27
    w /4.BOBHFE0CKFDUͷ͔ΘΓʹ/4.BOBHFE0CKFDU*%ΛϓϩύςΟ
    ͱͯ͠อ࣋͢Δɻ
    w /4.BOBHFE0CKFDU*%ʹ͸ܕ৘ใ͕ͳ͍ɻ
    w .BOBHFEPCKFDUʹΞΫηε͢Δʹ͸౎౓GFUDIͤͶ͹ͳΒͳ͍ɻ

    View Slide

  28. w /4.BOBHFE0CKFDUͷ͔ΘΓʹ/4.BOBHFE0CKFDU*%ΛϓϩύςΟ
    ͱͯ͠อ࣋͢Δɻ
    w /4.BOBHFE0CKFDU*%ʹ͸ܕ৘ใ͕ͳ͍ɻ
    w .BOBHFEPCKFDUʹΞΫηε͢Δʹ͸౎౓GFUDIͤͶ͹ͳΒͳ͍ɻ
    28
    4XJGU$PODVSSFODZͰ.BOBHFE0CKFDUΛ҆શʹอ͍࣋ͨ͠ʂ
    εϨουҧ൓Λ๷͙ैདྷख๏

    View Slide

  29. .BOBHFE0CKFDUΛ
    ҆શʹอ࣋͢Δʹ͸ʁ
    29

    View Slide

  30. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    30

    View Slide

  31. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    31

    View Slide

  32. 4FOEBCMFͱ͸ʁ
    w σʔλڝ߹͕ൃੜͤͣ҆શʹड͚౉͠Ͱ͖ΔܕΛද͢ϓϩτίϧɻ
    w ͲͷBDUPSDPOUFYU͔ΒΞΫηεͯ͠΋҆શɻ
    32

    View Slide

  33. 4FOEBCMFͱ͸ʁ
    w σʔλڝ߹͕ൃੜͤͣ҆શʹड͚౉͠Ͱ͖ΔܕΛද͢ϓϩτίϧɻ
    w ͲͷBDUPSDPOUFYU͔ΒΞΫηεͯ͠΋҆શɻ
    w ྫ͑͹ɺ஋ܕ͸4FOEBCMFɻ
    w ड͚౉࣌͠ʹίϐʔ͞ΕΔ͔Βɻ
    w /4.BOBHFE0CKFDU΍/4.BOBHFE0CKFDU$POUFYU͸
    4FOEBCMFͰͳ͍ɻ
    w ҟͳΔBDUPSDPOUFYU͔ΒΞΫηεͯ͠͸͍͚ͳ͍ɻ
    33

    View Slide

  34. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    34

    View Slide

  35. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w (MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    35

    View Slide

  36. (MPCBMBDUPSͱ͸ʁ
    w ϓϩύςΟ౳΁ͷಉظΞΫηεΛಛఆͷBDUPSDPOUFYUʹ੍ݶ͢Δɻ
    w ྫʣ.BJO"DUPSΛద༻ͨ͠ϓϩύςΟ͸ɺ.BJO"DUPS͔ΒͷΈ
    ಉظΞΫηεͰ͖Δɻ
    w /PO4FOEBCMFͳܕʹHMPCBMBDUPSΛద༻͢Δͱɺ4FOEBCMFͳܕ
    ͱͯ͠ѻ͏͜ͱ͕Ͱ͖Δɻ
    w (MPCBMBDUPSʹΑͬͯσʔλڝ߹͕ൃੜ͠ͳ͍͜ͱ͕อূ͞ΕΔ͔Βɻ
    36
    @MainActor var mainActorIsolatedValue: Int?

    View Slide

  37. (MPCBMBDUPSΛఆٛ͢Δ
    w .BOBHFE0CKFDU΍.0$Λ4FOEBCMFʹ͢ΔͨΊʹ
    HMPCBMBDUPSΛఆٛ͢Δɻ
    w (MPCBMBDUPSͱͯ͠ɺ$PSF%BUB"DUPSΛఆٛ͢Δɻ
    w 6*΁ͷӨڹΛ๷͙ͨΊɺ.BJO"DUPS͸࢖Θͳ͍ɻ
    37
    @globalActor
    public actor CoreDataActor {
    public static let shared = CoreDataActor()
    }

    View Slide

  38. .BOBHFE0CKFDUʹHMPCBMBDUPSΛద༻
    w .BOBHFE0CKFDUͷܕʢ$PSF%BUB&OUJUZʣʹ$PSF%BUB"DUPSΛ
    ద༻͢Δɻ
    w ܕͷఆٛʹ!$PSF%BUB"DUPSΛ͚ͭΔ͚ͩɻ
    w ͜ΕͰ.BOBHFE0CKFDUΛ4FOEBCMFͳ΋ͷͱͯ͠ѻ͑Δɻ
    38
    @objc(CoreDataEntity)
    @CoreDataActor
    public class CoreDataEntity: NSManagedObject {}

    View Slide

  39. .0$ʹHMPCBMBDUPSΛద༻
    w $PSF%BUB"DUPSΛద༻ͨ͠ϥού $".BOBHFE0CKFDU$POUFYU

    Λ༻ҙ͠ɺϥούܦ༝Ͱ.0$Λར༻͢Δɻ
    39
    @CoreDataActor final class CAManagedObjectContext {
    private let moc: NSManagedObjectContext
    init(_ moc: NSManagedObjectContext) {
    self.moc = moc
    }
    func fetch<…>(…) throws -> [T] { … }
    func save() throws { try moc.save() }
    func delete(_ object: NSManagedObject) { … }
    func insert() -> T { … }
    }

    View Slide

  40. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w (MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    40

    View Slide

  41. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w (MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    41

    View Slide

  42. ඞཁͳੑ࣭
    4XJGU$PODVSSFODZʹ͓͍ͯ.BOBHFE0CKFDUΛ҆શʹϓϩύςΟͱ
    ͯ͠อ࣋ɺड͚౉͢͠Δʹ͸ɺ.BOBHFE0CKFDUͱ.BOBHFE
    0CKFDU$POUFYUʢ.0$ʣ͕࣍ͷੑ࣭Λຬͨ͢ඞཁ͕͋Δɻ
    w 4FOEBCMFͰ͋Δɻ
    w (MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .BOBHFE0CKFDU͸ൃߦݩͷ.0$ͱಉ͡εϨουͰΞΫηε͞ΕΔɻ
    w $VTUPN"DUPS&YFDVUPSΛར༻͢Δɻ
    42

    View Slide

  43. "DUPSͷ࣮ߦ؀ڥ
    43
    actor MyActor { … }
    w "DUPS͸ɺσʔλڝ߹͕ൃੜ͠ͳ͍͜ͱΛอূ͢Δɻ
    w ಉظతʹ࣮ߦ͢Δ໋ྩͷ·ͱ·ΓͰ͋Δʮ+PCʯΛҰ࣮ͭͣͭߦɻ

    View Slide

  44. "DUPSͷ࣮ߦ؀ڥ
    44
    actor MyActor { … }
    w "DUPS͸ɺσʔλڝ߹͕ൃੜ͠ͳ͍͜ͱΛอূ͢Δɻ
    w ಉظతʹ࣮ߦ͢Δ໋ྩͷ·ͱ·ΓͰ͋Δʮ+PCʯΛҰ࣮ͭͣͭߦɻ
    w ʮ+PCʯΛͲͷΑ͏ʹ࣮ߦ͢Δ͔͸ఆٛ͞Ε͍ͯͳ͍ɻ
    w σϑΥϧτͰ͸ɺ4XJGUϥϯλΠϜ͕+PCͷ࣮ߦ؀ڥΛఏڙ͢Δɻ
    w +PC͕࣮ߦ͞ΕΔεϨουࣗମ͸ຖճมΘΔՄೳੑ͕͋Δɻ

    View Slide

  45. $VTUPN"DUPS&YFDVUPSͱ͸
    w 4& 4XJGUͰ࣮૷

    w "DUPSͷʮ+PCΛͲ͏࣮ߦ͢Δͷ͔ʯΛΧελϚΠζͰ͖Δɻ
    w +PCΛ࣮ߦ͢Δ&YFDVUPSΛ࣮૷͢Δɻ
    w VOPXOFE&YFDVUPSͱͯ͠ɺ+PCΛ࣮ߦ͢Δ&YFDVUPSΛొ࿥͢Δɻ
    45
    actor MyActor {
    nonisolated var unownedExecutor:
    UnownedSerialExecutor { ... }
    }

    View Slide

  46. ͲΜͳ&YFDVUPSΛ࣮૷͢΂͖͔
    w /4.BOBHFE0CKFDU$POUFYUQFSGPSN಺ͰॲཧΛߦ͍͍ͨɻ
    w NBOBHFEPCKFDUDPOUFYUΛอ࣋͢Δɻ
    w NBOBHFEPCKFDUDPOUFYUͷQFSGPSNͰ+PCΛશ࣮ͯߦ͢Δɻ
    w 4IBSFEΠϯελϯε͕ඞཁɻ
    w (MPCBMBDUPSʹొ࿥͢Δɻ
    46

    View Slide

  47. $PSF%BUB&YFDVUPSͷ࣮૷
    w KPCΛ/4.BOBHFE0CKFDU$POUFYUQFSGPSN @
    Ͱ࣮ߦ͢Δɻ
    47
    final class CoreDataExecutor: SerialExecutor {
    private let container: MOCContainer
    func enqueue(_ job: consuming ExecutorJob) {
    let unownedJob = UnownedJob(job)
    container.perform {
    unownedJob.runSynchronously(
    on: self.asUnownedSerialExecutor()
    )
    }
    }
    }

    View Slide

  48. $PSF%BUB&YFDVUPSͷ࣮૷
    48
    final class CoreDataExecutor: SerialExecutor {
    private let container: MOCContainer
    func enqueue(_ job: consuming ExecutorJob) {
    let unownedJob = UnownedJob(job)
    container.perform {
    unownedJob.runSynchronously(
    on: self.asUnownedSerialExecutor()
    )
    }
    }
    }
    w KPCΛ/4.BOBHFE0CKFDU$POUFYUQFSGPSN @
    Ͱ࣮ߦ͢Δɻ
    +PCΛQFSGPSNؔ਺಺Ͱ࣮ߦ

    View Slide

  49. $PSF%BUB&YFDVUPSͷ࣮૷
    49
    final class CoreDataExecutor: SerialExecutor {
    private let container: MOCContainer
    func enqueue(_ job: consuming ExecutorJob) {
    let unownedJob = UnownedJob(job)
    container.perform {
    unownedJob.runSynchronously(
    on: self.asUnownedSerialExecutor()
    )
    }
    }
    }
    w KPCΛ/4.BOBHFE0CKFDU$POUFYUQFSGPSN @
    Ͱ࣮ߦ͢Δɻ
    &YFDVUPS+PC͸/PODPQZBCMFUZQF
    ʢ4XJGUͰಋೖʣ

    View Slide

  50. $PSF%BUB&YFDVUPSͷ࣮૷
    50
    final class CoreDataExecutor: SerialExecutor {
    private let container: MOCContainer
    func enqueue(_ job: consuming ExecutorJob) {
    let unownedJob = UnownedJob(job)
    container.perform {
    unownedJob.runSynchronously(
    on: self.asUnownedSerialExecutor()
    )
    }
    }
    }
    w 4FSJBM&YFDVUPS͸4FOEBCMFͰ͋Δ͜ͱΛཁٻ͢Δɻ
    /4.BOBHFE0CKFDU$POUFYUΛ
    4FOEBCMFͳ΋ͷͱͯ͠ѻ͏ͨΊͷίϯςφ

    View Slide

  51. .0$$POUBJOFSͷ࣮૷
    w .0$ͷQFSGPSNΛεϨουηʔϑʹݺͿͨΊͷίϯςφ
    51
    final class MOCContainer: @unchecked Sendable {
    private let persistentContainer: NSPersistentContainer
    let queue = DispatchQueue(label: "MOCContainer")
    lazy var moc: NSManagedObjectContext =
    persistentContainer.newBackgroundContext()
    init(_ persistentContainer: NSPersistentContainer) {
    self.persistentContainer = persistentContainer
    }
    func perform(_ block: @escaping @Sendable () -> Void) {
    queue.async { self.moc.perform(block) }
    }
    }

    View Slide

  52. $PSF%BUB&YFDVUPS͖ͭͮ
    w (MPCBMBDUPSʹ&YFDVUPSΛొ࿥͢ΔͨΊʹɺTIBSFEΠϯελϯεΛ
    ༻ҙ͢Δɻ
    52
    extension CoreDataExecutor {
    @CoreDataActor var moc: CAManagedObjectContext {
    .init(container.moc)
    }
    static var sharedExecutor = CoreDataExecutor.init(.shared)
    static var sharedUnownedExecutor: UnownedSerialExecutor {
    sharedExecutor.asUnownedSerialExecutor()
    }
    }

    View Slide

  53. (MPCBMBDUPSʹ&YFDVUPSΛొ࿥
    w $PSF%BUB"DUPSͷVOPXOFE&YFDVUPSͱͯ͠ɺ
    $PSF%BUB&YFDVUPSͷTIBSFEΠϯελϯεΛฦ͢Α͏ʹ͢Δɻ
    w $PSF%BUB"DUPSʹର͢Δૢ࡞͸͢΂ͯɺ$PSF%BUB&YFDVUPS͕؅ཧ
    ͢Δ.0$ͷQFSGPSNؔ਺Ͱ࣮ߦ͞ΕΔΑ͏ʹͳͬͨɻ
    53
    extension CoreDataActor {
    public nonisolated var unownedExecutor:
    UnownedSerialExecutor {
    CoreDataExecutor.sharedUnownedExecutor
    }
    }

    View Slide

  54. ͜͜·Ͱͷ·ͱΊ
    .BOBHFE0CKFDUͱ.0$ʹରͯ͠ҎԼͷ͜ͱΛߦͳͬͨɻ
    w 4FOEBCMFʹ͢Δɻ
    w .BOBHFE0CKFDUʜ(MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .0$ʜ(MPCBMBDUPSΛద༻ͨ͠ϥούΛ࣮૷ɻ
    w (MPCBMBDUPSͷ+PCͷ࣮ߦΛ੍ݶɻ
    w $VTUPN"DUPS&YFDVUPSΛར༻͢Δɻ
    w &YFDVUPSͷڞ௨.0$ͷQFSGPSNؔ਺Ͱ͢΂ͯͷ+PCΛ࣮ߦɻ
    54

    View Slide

  55. ͜͜·Ͱͷ·ͱΊ
    .BOBHFE0CKFDUͱ.0$ʹରͯ͠ҎԼͷ͜ͱΛߦͳͬͨɻ
    w 4FOEBCMFʹ͢Δɻ
    w .BOBHFE0CKFDUʜ(MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .0$ʜ(MPCBMBDUPSΛద༻ͨ͠ϥούΛ࣮૷ɻ
    w (MPCBMBDUPSͷ+PCͷ࣮ߦΛ੍ݶɻ
    w $VTUPN"DUPS&YFDVUPSΛར༻͢Δɻ
    w &YFDVUPSͷڞ௨.0$ͷQFSGPSNؔ਺Ͱ͢΂ͯͷ+PCΛ࣮ߦɻ
    55

    View Slide

  56. ͜͜·Ͱͷ·ͱΊ
    .BOBHFE0CKFDUͱ.0$ʹରͯ͠ҎԼͷ͜ͱΛߦͳͬͨɻ
    w 4FOEBCMFʹ͢Δɻ
    w .BOBHFE0CKFDUʜ(MPCBMBDUPSΛܕʹద༻͢Δɻ
    w .0$ʜ(MPCBMBDUPSΛద༻ͨ͠ϥούΛ࣮૷ɻ
    w (MPCBMBDUPSͷ+PCͷ࣮ߦΛ੍ݶɻ
    w $VTUPN"DUPS&YFDVUPSΛར༻͢Δɻ
    w &YFDVUPSͷڞ௨.0$ͷQFSGPSNؔ਺Ͱ͢΂ͯͷ+PCΛ࣮ߦɻ
    56

    View Slide

  57. ͜ΕͰεϨουηʔϑʹͳͬͨͷ͔ʁ
    ࠷΋جຊతͳ$36%ૢ࡞ͦΕͧΕʹ͍ͭͯɺ
    w $PSF%BUB"DUPS಺͔ΒͷΞΫηε
    w $PSF%BUB"DUPS֎͔ΒͷΞΫηε
    ͷ৔߹ʹɺͲͷΑ͏ʹίϯύΠϥʹνΣοΫ͞ΕΔ͔Λݟ͍ͯ͘ɻ
    57

    View Slide

  58. $SFBUFૢ࡞ʢ$PSF%BUB"DUPS಺ʣ
    w .0$͸ɺ$PSF%BUB&YFDVUPS͕ఏڙ͢Δ
    $".BOBHFE0CKFDU$POUFYUΦϒδΣΫτΛ༻͍Δɻ
    w $PSF%BUB"DUPS಺ͳͷͰɺಉظతʹૢ࡞Ͱ͖Δɻ
    58
    @CoreDataActor func createObject() throws -> Entity {
    let moc = CoreDataExecutor.sharedExecutor.moc
    let managedObject: Entity = moc.insert()
    try moc.save()
    return managedObject
    }

    View Slide

  59. @AnotherActor func createObject() throws -> Entity {
    let moc = CoreDataExecutor.sharedExecutor.moc
    let managedObject: Entity = moc.insert()
    try moc.save()
    return managedObject
    }
    $SFBUFૢ࡞ʢ$PSF%BUB"DUPS֎ʣ
    59
    ίϯύΠϧΤϥʔ
    BXBJU͕ඞཁ
    w .BOBHFEPCKFDU͓Αͼ.0$͸ɺඇಉظΞΫηε͕ڧ੍͞ΕΔ

    View Slide

  60. $SFBUFૢ࡞ʢ$PSF%BUB"DUPS֎ʣ
    60
    @AnotherActor func createObject() async throws -> Entity {
    let moc = await CoreDataExecutor.sharedExecutor.moc
    let managedObject: Entity = await moc.insert()
    try await moc.save()
    return managedObject
    }
    w .BOBHFEPCKFDU͓Αͼ.0$͸ɺඇಉظΞΫηε͕ڧ੍͞ΕΔ

    View Slide

  61. 3FBEૢ࡞ʢ$PSF%BUB"DUPS಺ʣ
    61
    @CoreDataActor func fetchObject() throws -> Entity? {
    let moc = CoreDataExecutor.sharedExecutor.moc
    let fetchRequest = Entity.fetchRequest()
    fetchRequest.entity = Entity.entity()
    let managedObjects = try moc.fetch(fetchRequest)
    return managedObjects.first
    }
    w .0$͸ɺ$PSF%BUB&YFDVUPS͕ఏڙ͢Δ
    $".BOBHFE0CKFDU$POUFYUΦϒδΣΫτΛ༻͍Δɻ
    w $PSF%BUB"DUPS಺ͳͷͰɺಉظతʹૢ࡞Ͱ͖Δɻ

    View Slide

  62. 3FBEૢ࡞ʢ$PSF%BUB"DUPS֎ʣ
    w /4'FUDI3FRVFTU͸4FOEBCMFͰͳ͍ɻ
    w $PSF%BUB"DUPS಺Ͱ૊Έཱͯ.0$ʹ౉͢͜ͱ͕ඞཁɻ
    62
    @AnotherActor func fetchObject() async throws -> Entity? {
    let moc = await CoreDataExecutor.sharedExecutor.moc
    let managedObjects = try await Task { @CoreDataActor in
    let fetchRequest = Entity.fetchRequest()
    fetchRequest.entity = Entity.entity()
    return try moc.fetch(fetchRequest)
    }.value
    return managedObjects.first
    }

    View Slide

  63. 6QEBUFૢ࡞ʢ$PSF%BUB"DUPS಺ʣ
    63
    @CoreDataActor
    func updateObject(managedObject: Entity) throws {
    // nameΛupdate͢Δ
    let moc = CoreDataExecutor.sharedExecutor.moc
    managedObject.name = "Jiro"
    try moc.save()
    }
    w .0$͸ɺ$PSF%BUB&YFDVUPS͕ఏڙ͢Δ
    $".BOBHFE0CKFDU$POUFYUΦϒδΣΫτΛ༻͍Δɻ
    w $PSF%BUB"DUPS಺ͳͷͰɺಉظతʹૢ࡞Ͱ͖Δɻ

    View Slide

  64. 6QEBUFૢ࡞ʢ$PSF%BUB"DUPS֎ʣ
    w 4FUUFS͸BDUPS֎ʹެ։͞Εͳ͍ͨΊɺ$PSF%BUB"DUPS಺Ͱͷૢ࡞
    ͕ڧ੍͞ΕΔɻ
    64
    @AnotherActor
    func updateObject(managedObject: Entity) async throws {
    // nameΛupdate͢Δ
    try await Task { @CoreDataActor in
    let moc = CoreDataExecutor.sharedExecutor.moc
    managedObject.name = "Jiro"
    try moc.save()
    }.value
    }

    View Slide

  65. %FMFUFૢ࡞ʢ$PSF%BUB"DUPS಺ʣ
    65
    @CoreDataActor
    func deleteObject(managedObject: Entity) throws {
    let moc = CoreDataExecutor.sharedExecutor.moc
    moc.delete(managedObject)
    try moc.save()
    }
    w .0$͸ɺ$PSF%BUB&YFDVUPS͕ఏڙ͢Δ
    $".BOBHFE0CKFDU$POUFYUΦϒδΣΫτΛ༻͍Δɻ
    w $PSF%BUB"DUPS಺ͳͷͰɺಉظతʹૢ࡞Ͱ͖Δɻ

    View Slide

  66. %FMFUFૢ࡞ʢ$PSF%BUB"DUPS֎ʣ
    66
    @AnotherActor
    func deleteObject(managedObject: Entity) async throws {
    let moc = await CoreDataExecutor.sharedExecutor.moc
    await moc.delete(managedObject)
    try await moc.save()
    }
    w .BOBHFEPCKFDU͓Αͼ.0$͸ɺඇಉظΞΫηε͕ڧ੍͞ΕΔ

    View Slide

  67. ࠷ॳͷίʔυ͸Ͳ͏ͳͬͨͷ͔
    67
    class CoreDataController {
    private var entity: CoreDataEntity? = nil
    private let moc: NSManagedObjectContext
    init(container: NSPersistentContainer) {
    self.moc = container.newBackgroundContext()
    }
    func configure() throws {
    entity = CoreDataEntity(context: moc)
    try moc.save()
    }
    }

    View Slide

  68. ͜͏ͳΓ·ͨ͠
    68
    @CoreDataActor class CoreDataController {
    private var entity: CoreDataEntity? = nil
    func configure() throws {
    let moc = CoreDataExecutor.sharedExecutor.moc
    entity = moc.insert()
    try moc.save()
    }
    }

    View Slide

  69. ͜͏ͳΓ·ͨ͠
    69
    @CoreDataActor class CoreDataController {
    private var entity: CoreDataEntity? = nil
    func configure() throws {
    let moc = CoreDataExecutor.sharedExecutor.moc
    entity = moc.insert()
    try moc.save()
    }
    }
    w $MBTTࣗମΛ$PSF%BUB"DUPSʹॴଐͤ͞Δɻ
    w ҆શʹNBOBHFEPCKFDUΛϓϩύςΟͱͯ͠อ࣋ɻ

    View Slide

  70. ·ͱΊ
    w NBOBHFEPCKFDUͱNBOBHFEPCKFDUDPOUFYUΛɺHMPCBMBDUPS
    Λ༻͍ͯεϨουηʔϑʹ͢Δ͜ͱΛࢼΈͨɻ
    w $VTUPNBDUPSFYFDVUPSΛར༻͢Δ͜ͱͰɺ࣮ߦεϨουͷಉҰ
    ੑΛ࣮ݱͰ͖Δɻ
    w $36%ૢ࡞ʹ͍ͭͯɺBDUPSڥք಺͔Β͸શͯಉظతʹૢ࡞͕Մೳͳ
    ҰํͰɺBDUPSڥք֎͔Β͸ඇಉظΞΫηεΛཁٻͰ͖ͨɻ
    70

    View Slide