🔖

【Swift】@_implicitSelfCaptureについて

2024/06/25に公開

@_implicitSelfCaptureとは

escaping属性が指定されているクロージャ内でselfを参照し、そのselfが参照型である場合は基本的にselfを付けて明示的にキャプチャを行わなければなりません。

この制約を排除できるのが@_implicitSelfCaptureです。

以下のコードでは、escaping属性が指定されているクロージャ内でselfを参照していますが、明示的なキャプチャが行われていないため、コンパイルエラーが発生しています。

var someAction: () -> Void = {}

func setAction(_ action: @escaping () -> Void) {
    someAction = action
}

class Sample {
    var value = 0

    func action() {
        setAction {
            value = 777 // 【Error】
        }
    }
}
// ※ メモリリークに関しては無視してください。

setActionの引数に@_implicitSelfCaptureを付与すると、上記のエラーは発生しなくなります。

var someAction: () -> Void = {}

func setAction(@_implicitSelfCapture _ action: @escaping () -> Void) { // 変更
    someAction = action
}

class Sample {
    var value = 0

    func action() {
        setAction {
            value = 777
        }
    }
}
// ※ メモリリークに関しては無視してください。

@_implicitSelfCaptureはどこで使われているのか

@_implicitSelfCaptureTaskのイニシャライザの引数operationなどに対して付けられています。

そのためTask内では、明示的なキャプチャを示すためのselfを記入する必要はありません。

@MainActor
final class ViewModel: ObservableObject {
    @Published private(set) var num = 0

    func action() {
        Task {
            try? await Task.sleep(for: .seconds(3))
            num += 1
        }
    }
}

※ 本題から逸れますが、operationがViewModelインスタンスを強参照するため、解放遅延が生じる可能性があります。

参考

https://github.com/swiftlang/swift/blob/main/docs/ReferenceGuides/UnderscoredAttributes.md?ref=blog.personal-factory.com#_implicitselfcapture

Discussion