Swift
Xcode7.3

Swiftのクラスの一番最初のプロパティを非ObjCの型にするとEXC_BAD_ACCESSする件

More than 1 year has passed since last update.

ちょっと怖すぎんだろうと思ったので記事にします。

なお、このバグはこちらでレポートされています。

https://bugs.swift.org/browse/SR-1055


発生している問題

サンプルコードはこちらです。https://github.com/fmtonakai/SwiftPropertyCrash7_3

Swiftで次のようなクラスを定義します。

enum MyEnum {

case Foo
}

class MyObject: NSObject {
let myEnum: MyEnum = .Foo
}

これを利用すると実行時にクラッシュします。

スクリーンショット 2016-03-26 15.31.49.png

なお、ObjCとSwiftの混合プロジェクトで実行してますが、Swift onlyのプロジェクトでも同様にクラッシュが発生します。


回避方法

なんでもいいのでmyEnumの前にObjCからアクセスできるプロパティを設定します。もしくはプロパティの順番を変えて最初にObjCからアクセスできるものを一番最初に持ってきます。(どういうことなの・・・)

class MyObject: NSObject {

let str = "str"
let myEnum: MyEnum = .Foo
}


対策


自分のアプリがクラッシュするかを調べる

実行時エラーであり、問題のあるクラスを生成した時にクラッシュするので、アプリのどこでクラッシュが起こるかわかりません。アプリの中に爆弾を抱えた状態になります。

ここで、アプリがこれによってクラッシュするかを検出する方法があります。

それはObjC runtime関数のobjc_copyClassListを呼ぶことです。

スクリーンショット 2016-03-26 15.46.18.png

このように問題のあるクラスがあるとobjc_copyClassListでBAD_ACCESS起こすので、この問題を引き起こすクラスがあることを検出できます。クラッシュしなければとりあえず安心です。(がこれから作るクラスには十分注意してください)


クラッシュを引き起こすクラスを探す

クラッシュした場合、問題のあるクラスを探す必要があります。その時、この方法が便利です。

環境変数にOBJC_PRINT_CLASS_SETUP: YESを設定します

スクリーンショット 2016-03-26 16.01.02.png

すると起動時にObjCのクラスのセットアップがトレースされますが、問題のあるクラスがあった場合、そこでトレースが止まります。

クラッシュ時のログ

objc[24395]: CLASS: realizing class 'PropertyCrash.MyObject'  0x1002b0ec8 0x1002aff18

objc[24395]: CLASS: realizing class 'PropertyCrash.MyObject' (meta) 0x1002c5a88 0x1002afe88
objc[24395]: CLASS: methodizing class 'PropertyCrash.MyObject' (meta)

正常時のログ

objc[24453]: CLASS: realizing class 'PropertyCrash.MyObject'  0x1002b0f30 0x1002aff80

objc[24453]: CLASS: realizing class 'PropertyCrash.MyObject' (meta) 0x1002c5af8 0x1002afe88
objc[24453]: CLASS: methodizing class 'PropertyCrash.MyObject' (meta)
objc[24453]: CLASS: methodizing class 'PropertyCrash.MyObject'

methoding classが2度行われないクラスが問題のあるクラスとわかります。


まとめ

早くHotfix出てくれー!つらい・・・