soranoba
soranoba Author of soranoba.net
programming

SwiftとnilError

先日、AVAudioFile.read(into:frameCount:)の吐く例外がnilErrorという不可解なものになることがあることを知った.
最初は何故nilなのかと首を傾げたが、どうもnilErrorというエラーらしい. 不可解だったので調べてみることにした.

Foundation._GenericObjCError.nilError

このnilErrorは正式にはFoundation._GenericObjCError.nilErrorというものであり, Foundationのprivateな型なようだった.
このエラーはObjective-CのAPIをSwiftから呼び出した場合に発生する可能性があり, 以下のコードで再現できる.

+ (BOOL)execute:(NSError* _Nullable *_Nullable)error
{
    return NO;
}

Objective-CでNSError**を引数に取る場合に, Swiftではthrowsへと自動的に変換される.
しかしながら, 返り値がゼロ値 (nilNO) であっても実装次第では (上記コードのように) エラーの代入を行わないということが可能であり, その際にnilにならないように自動的に代入されるエラーがFoundation._GenericObjCError.nilErrorのようだ.

公開されていない型である以上, Swift上で確実に判別する方法は恐らく存在せず, typeなどを文字列にすることで判別するなどの方法が考えられる.
参考までに以下のような値をそれぞれ返す.

String(describing: type(of: error))  : _GenericObjCError
"\(error)"                           : nilError
type(of: error)                      : _GenericObjCError
error.localizedDescription           : The operation couldn’t be completed. (Foundation._GenericObjCError error 0.)


返り値がゼロ値でありながらエラーを代入しないのは適切か

nilErrorの正体は前述の通りだが、ここで気になるのはゼロ値を返しつつエラーを代入しないのは適切なのか、という点である.
例えば, 前述のAVAudioFile.read(into:frameCount:)であれば1フレームも読み込めなかった場合はどう扱うかという話になる.
Swiftだと返り値でboolを返しつつ, 例外を投げれば良さそうだろう. なんだったら読み込んだフレーム数をintで返しても良い.

そう考えると, Objective-CからSwiftに変換することでこの情報が欠落していることが問題なように感じてくる.
とはいえ, 言っても詮ないことなのでObjective-Cで実装する際は, 大人しくゼロ値を返すときはエラーを代入するようにした方が良さそうです.

参考

(Updated: )

comments powered by Disqus