0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftのProtocolで@objcとTyped throwsを併用するとコンパイラーがクラッシュする

Posted at

はじめに

Swiftでプロトコルを定義し、Objective-Cからも利用可能にするために @objc 属性を付けることがあります。その際、プロトコルメソッドで特定の型のエラーをスローする Typed throws を使おうとすると、Swiftコンパイラ自体がクラッシュしてビルドが失敗することがあります。

発生バージョン

XCode 16.2
Swift version 6.0.3 (5.10 モード)

コンパイラ自体がクラッシュする条件

以下のように、@objc 属性が付いたプロトコル HogeProtocol を定義し、そのメソッド HogeFunc で特定の FugaError 型のみをスローするように throws(FugaError) と指定したとします。

import Foundation

// エラー型を定義
enum FugaError: Error {
    case someError
}

// @objc属性付きのプロトコルを定義
@objc
protocol HogeProtocol {
    // Typed throwsを宣言
    func HogeFunc() throws(FugaError) // <- ここが問題
}

// プロトコルに準拠したクラス
final class Hoge: HogeProtocol {
    func HogeFunc() throws(FugaError) {
        // 何らかの処理
        throw FugaError.someError
    }
}

このコードをコンパイルしようとすると、以下のような問題が発生します。

Swiftコンパイラ (swift-frontend) が内部でエラーを起こしてクラッシュし、ビルドが失敗します。

Command EmitSwiftModule failed with a nonzero exit code

詳細なログを見ると、以下のようなスタックトレースが出力されていました。

Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.	Program arguments: /Applications/Xcode-16.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend ... (略) ...
1.	Apple Swift version 6.0.3 (...)
2.	Compiling with effective version 5.10
3.	While generating Clang header 
4.	While printing forward declarations needed by members of 'HogeProtocol' (...)
5.	While printing for member 'HogeFunc()' (...) 
6.	While walking its interface type, currently at 'FugaError' (...) 
7.	While printing NULL type! 
... (以下スタックトレース) ...

おそらくの原因

根本的な原因は @objc プロトコル内で Typed throws を使用していることにあります。

Objective-Cの NSError* には、Swiftの throws(SpecificError) のような「特定の型のエラーのみをスローする」という概念がありません。Objective-Cから見ると、メソッドがエラーをスローするかどうかは分かりますが、そのエラーの具体的な型まではコンパイル時には分かりません。

そのため、@objc で公開されるプロトコルのメソッド定義で Typed throws を使うと、Objective-Cとの互換性が取れず、Objective-Cヘッダー生成の段階でコンパイラがクラッシュするようです。

解決策

この問題を解決するには、@objc プロトコルのメソッド定義では、型を指定せずに throws だけを使用します。

import Foundation

enum FugaError: Error {
    case someError
}

@objc
protocol HogeProtocol {
    // 型を指定せず、単に throws と記述する
    func HogeFunc() throws
}

final class Hoge: HogeProtocol {
    // 実装クラス側では、throws(FugaError) と型を指定しても良いし、
    // 単に throws としても良い。
    func HogeFunc() throws(FugaError){ 
        print("HogeFunc called")
        throw FugaError.someError
    }
}

ポイント:

  • プロトコル定義: @objc プロトコル内では throws のみを使用します
  • クラス実装: プロトコルに準拠するクラス側では、メソッドの実装で throws(SpecificError) と型を指定しても問題ありません

まとめ

Swiftで @objc 属性を付けたプロトコルを定義する際には Typed throws の使用はできません。
Objective-Cとの連携が必要な場面でエラーハンドリングを含むプロトコルを設計する際には注意してください

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?