[iOS] ‘weak’ cannot be applied to a property declaration in a protocol というエラーがあった場合の対応

はじめに

結論から言うと、Protocol での記述は weak は特に気にしなくてよさそうです。
Protocol では実クラスのプロパティが weakstrong かには関心がありません。


Swift において、weak var のプロパティを持つプロトコルを定義したくなった場合、
本ポストのタイトルのようなエラーが出ると思います。

例えば delegate を持つ Protocol を定義したい場合があると思います。

protocol BProtocol {
    weak var delegate: AProtocol? { get set }
}

実クラスでは delegate は weak var で宣言しないと循環参照となってしまいます。

ですが、Protocol での記述は weak は気にしなくてよさそうです。

簡単なプログラムで検証します。

前提条件

Mac: macOS Catalina 10.15.6
Xcode: Version 12.4 (12D4e)

検証

先に言ったように、Protocol での記述は weak は特に気にしなくてよさそうです。

以下のようなサンプルプログラムで検証します。

protocol AProtocol: AnyObject {
    func doSometing()
}

final class A: NSObject, AProtocol {
    var b: B?
    func raiseB() -> B? {
        b = B()
        b?.delegate = self
        
        return b
    }
    func doSometing() {}
}

protocol BProtocol {
    var delegate: AProtocol? { get set }
}

final class B: BProtocol {
    weak var delegate: AProtocol? // <- weak を外すと循環参照になります
    
    func printDelegate() {
        print("delegate: \(delegate)")
    }
}

ここで注目したいのが class B が持っている delegate です。

delegate プロパティはAProtocol に準拠したクラスの変数です。

BProtocol では delegate プロパティを weak 宣言していません(できません)。
が、実クラスのBでは weak 宣言しています。

最も単純なコードだと、A のインスタンスがnil になった時点で、
Bが保持している delegate プロパティも weak であれば、 nil になるはるはずです。

以下のようなコードを書いたとします。
「コードX」とします。

// コードX

var a: A? = A()
let b = a?.raiseB()
print("最初のprint")
b?.printDelegate()
        
a = nil
print("二番目のprint")
b?.printDelegate()

a = nil を実行された時点で、b が保持している delegate プロパティも nil になるはずです。

実際にそのようになります。

最初のprint
delegate: Optional(<HogeApp.A: 0x6000010f4320>)
二番目のprint
delegate: nil

一方、実クラスのB の delegate から weak を外してみます。
BProtocol では delegate プロパティを weak 宣言していません(できません)。

final class B: BProtocol {
    var delegate: AProtocol?
    ... 略 ...
}

これで「コードX」を実行します。

すると以下のようになります。

最初のprint
delegate: Optional(<HogeApp.A: 0x6000018b4530>)
二番目のprint
delegate: Optional(<HogeApp.A: 0x6000018b4530>)

つまり、Protocol では実クラスが weakstrong かには関心がない
ということになります。

当該のプロポーザル

Swift evolution に以下のプロポーザルがあります。
0186-remove-ownership-keyword-support-in-protocols.md

Protocol は weak だろうが strong だろうが宣言できるが、
実クラスではそれに従う必要はないため、混乱を招いてしまう。
そのため、そもそもProtocol ではそれらの宣言をできないようにするべきだ、と書かれています。

最後に

というわけで Protocol は所有権に関心がないということが分かりました。

以上です。
お疲れ様でした!

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。