Edited at

Xcode7.3(Swift2.2)のデバッグビルドで特定環境に発生するクラッシュ

More than 3 years have passed since last update.

こちらの記事で話題になっているものと微妙に違う(?)ような気がしていて、原因がわからなかったので、メモっておきます。

http://cjwirth.com/2016/03/26/xcode-7-3-crashing/

今のところiPad2(iOS7.0.2)とiPhone4s(7.1.2)のみでデバッグビルド時にクラッシュを確認しました。iPad4(iOS8.2)やiPhone6s(iOS9.3)などではクラッシュしないことが確認できています。


クラッシュするコード

以下のように、プロパティにジェネリクスのクラスを持ってるクラスを、さらにViewController中でプロパティとして持たせると、Xcode7.3に上げた時に上記の環境でクラッシュするようになりました。

// 1. ジェネリクスを持っていて、何かプロパテイを持つクラスを作成

class SomeEntity<T> {
let str = ""
}

// 2. そのクラスをプロパティとして持つSomeModelを作成
class SomeModel {
let entity = SomeEntity<Int>()
}

import UIKit

// 3. SomeModelをViewControllerのプロパティとして持たせる
class ViewController: UIViewController {
...
private let model = SomeModel() // <- ここで落ちる
...
}


スタックトレース

その時発生したクラッシュのスタックトレースは以下になります。

* thread #1: tid = 0x1aa2b, 0x0025cd64 libswiftCore.dylib`swift_initClassMetadata_UniversalStrategy + 440, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x52de4)

* frame #0: 0x0025cd64 libswiftCore.dylib`swift_initClassMetadata_UniversalStrategy + 440
frame #1: 0x0004e4c4 Test5`___lldb_unnamed_function2$$Test5 + 180
frame #2: 0x0025e07e libswiftCore.dylib`(anonymous namespace)::GenericCacheEntry* llvm::function_ref<(anonymous namespace)::GenericCacheEntry* ()>::callback_fn<swift_getGenericMetadata::$_1>(long) + 18
frame #3: 0x0025df72 libswiftCore.dylib`swift::MetadataCache<(anonymous namespace)::GenericCacheEntry>::addMetadataEntry(swift::EntryRef<(anonymous namespace)::GenericCacheEntry>, ConcurrentList<swift::MetadataCache<(anonymous namespace)::GenericCacheEntry>::EntryPair>&, llvm::function_ref<(anonymous namespace)::GenericCacheEntry* ()>) + 80
frame #4: 0x0025bf28 libswiftCore.dylib`swift::MetadataCache<(anonymous namespace)::GenericCacheEntry>::findOrAdd(void const* const*, unsigned long, llvm::function_ref<(anonymous namespace)::GenericCacheEntry* ()>) + 308
frame #5: 0x0025c024 libswiftCore.dylib`swift_getGenericMetadata2 + 66
frame #6: 0x0004e6f8 Test5`type metadata accessor for SomeEntity<Int> + 88 at ViewController.swift:0
...
...


対策

現状では次のように書き換えると落ちなくなるのを確認しています。


  • SomeEntityを構造体にすると大丈夫

  • SomeEntityにプロパティを持たせなければ大丈夫

  • SomeModelの持ってるプロパティをlazyにすると大丈夫

  • ViewController内でのSomeModelの初期化をViewDidLoadの中でやったら大丈夫


原因

今のところよくわかっていません。


swift.orgのバグレポートで相談中

対策はわかったのですが根本的な原因は結局わからなかっため、同僚の@chocoyamaがbugs.swift.orgのバグレポートでチケットを立ててくれました。まだ返事はありませんが、アップデートがあったら記事を更新します。

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


追記1 (2016/4/1)

バグレポートについているコメントによると、以下のコードでも同じエラーでクラッシュするそうです。


Yep, I have met the same crash running on iOS7.1.2 with swift 2.2.

let objectMirror = Mirror(reflecting: self) 

https://bugs.swift.org/browse/SR-1096?focusedCommentId=13252&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13252



追記2 (2016/4/10)

swift 2.2のバグであることがわかり、コミッターの方が原因の調査・修正をしてくださいました。既にswift2.2のmasterブランチにはマージされておりますが、Xcodeに組み込まれてリリースされる時期は不明です。


Found the bug. HeapObject.h and SwiftObject.mm disagree about SwiftObject's contents on 32-bit.

https://bugs.swift.org/browse/SR-1096?focusedCommentId=13530&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13530


修正差分

https://github.com/apple/swift/commit/41b12e7929407903c42788261c2e6338a7b5cec6

コードは全然理解できていませんが、コメントでのやり取り結果を自分なりに解釈すると、32-bitで発生していたSwiftのバグがiOS7のARC周りの処理を通った時にクラッシュを引き起こしていたようです。

従って、iOS7をサポートしているアプリは注意する必要がありそうです。


追記3 (2016/5/9)

Xcode 7.3.1でクラッシュが発生せず、上記修正コードが入っていることを確認できました。この現象でのクラッシュは解決されたようです。

https://bugs.swift.org/browse/SR-1096?focusedCommentId=14358&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14358


追記4 (2016/6/2)

コメント欄で教えて頂いたのですが、Xcode 7.3.1でもこちらのバグレポートに書いてある形でGenericsを使うとiPhone4s, iOS7.1.2でまだ同じようなクラッシュをするようです...

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