0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift の型制約付きプロトコルの実行時エラー

Posted at

先日、自分が発見した型制約付きプロトコルの実行時エラーの説明とその解消法について紹介します。

型制約付きプロトコル

Swift の Protocol は、宣言時に where で準拠する型に制約を指定することができます。
準拠する型が制約を満たしていないと、コンパイラが、コンパイル時にエラーとして検知してくれます。

型制約付きプロトコルの例
class SuperClass { }

// 型制約付きプロトコル
protocol SomeProtocol where Self: SuperClass { // 準拠する型は SuperClass を継承するように制約を付ける
    func doSomething()
}

class SomeSubClass: SuperClass, SomeProtocol {
    func doSomething() {
      print("doSomething")
    }
}

class InvalidSomeSubClass: SomeProtocol { // コンパイルエラー: 'SomeProtocol' requires that 'InvalidSomeClass' inherit from 'SuperClass'
    func doSomething() {
      print("doSomething")
    }
}

型制約付きプロトコルによる実行時エラー

型制約付きプロトコルは、準拠する型に制約を付け、コンパイル時にその制約を検知できる便利な機能です。
しかし、型制約付きプロトコルゆえの実行時エラーも存在します。

実行時エラーの例
class SuperClass { }

protocol SomeProtocol where Self: SuperClass {
    func doSomething()
}

class SomeSubClass: SuperClass, SomeProtocol {
    func doSomething() {
      print("doSomething")
    }
}

let sp: SomeProtocol = SomeSubClass() // SomeProtocol 型として保持する
sp.doSomething() // EXC_BAD_ACCESS で落ちる

エラーの原因は、SomeSubClass のインスタンスを SomeProtocol (型制約付きプロトコル) 型として保持して、そのメソッドを呼び出したことです。
SomeSubClassSomeProtocol に準拠しているため、そのインスタンスを SomeProtocol 型として扱ってもコンパイルは通ります。
しかし、インスタンス sp 自体は、 SuperClass を継承した型ではありません。そのため、 spdoSomething() に実行時にアクセスすると、型制約を満たしていないため、エラーとして落ちてしまいます。

実行時エラーの解決

上記の実行時エラーを解決するための方法は、2通りあります。

1. インスタンスを型制約の型として実行する

let sp: SuperClass & SomeProtocol = SomeSubClass() // SuperClass & SomeProtocol 型として保持する
sp.doSomething()  // "doSomething"

インスタンス sp が実行時に型制約を満たしていないことがエラーとなっていたため、 sp を型制約を満たすような型として実行すれば解決します。

2. 型制約付きプロトコルに @objc 属性を適用する

@objc protocol SomeProtocol where Self: SuperClass { // @objc を適用
    func doSomething()
}
...
let sp: SomeProtocol = SomeSubClass() // SomeProtocol 型として保持する
sp.doSomething() // "doSomething"

なぜ @objc の適用によって実行時エラーを回避するか理解はできていませんが、解決法として紹介されていました。
https://bugs.swift.org/browse/SR-7068

まとめ

エラー文が EXC_BAD_ACCESS としか表示されず、原因を突き止めるのに時間がかかりました。Swift の Protocol は型制約を指定することができますが、実行時のエラーも考慮して使いましょう。

参考

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?