2017年 10月

 色々と手詰まり中 / magicien 

ここのところ色々なバグや問題に引っかかってあらゆるものが停滞している。以下、駄文。

iOSでclassの変数にアクセスすると、EXC_BAD_ACCESSエラーで落ちる

JSONDecoderで取得したデータにアクセスしようとすると、エラーで落ちる問題があって困惑している。
考えられる原因としては、
  • MainThread以外でJSONDecoderを使っているから?
  • Codableなclassをextendsして使っているから?
  • ARCの処理が変わった?どこかをweakとかunownedとかにしないといけないのか?
  • JSONDecoderかARCのバグ?
といった感じなのだけれど、よくわからない。コールスタックを見ると、こんな感じの見知らぬ関数が呼ばれていた。

  • materializeForSet
  • swift_beginAccess
  • insert
  • reportExclusivityConflict
JSONDecoderで取得したデータにアクセスした時に、排他アクセスチェックが行われ、問題があったのでデバッガにリポートを送信した帰りに変なメモリにアクセスして落ちているようだ。実際は、reportExclusivityConflictが終わり、insertから帰る寸前で何かをやらかしている。スタックに退避したデータをレジスタに書き戻す直前に何かごにょごにょやっているようなのだが、処理を追う気力が無かった...
まぁ元々の問題はどこかで変数書き込み処理がコンフリクトしていることらしいので、それを解決すれば、この問題も自ずと解決するはず。せめて、reportExclusivityConflictの出力がデバッガで受け取れれば良いのだが、その前にエラーで落ちてしまっているようで、原因の究明が難航している。

SCNSkinnerが使えなくなった

ベータ版を使っていた時は大丈夫だったのに。
SCNSkinnerを使うには、boneIndicesとboneWeightsを渡す必要があるのだが、boneIndicesにいつも通りUInt16のデータを渡すと、
[SceneKit] Error: Compiler error while building render pipeline state for node "(null)" (0x1016db2c0): Error Domain=CompilerError Code=2 "Vertex attribute skinningJoints(4) of type uint4 cannot be read using MTLAttributeFormatShort4" UserInfo={NSLocalizedDescription=Vertex attribute skinningJoints(4) of type uint4 cannot be read using MTLAttributeFormatShort4}
というエラーが出る。uint4は4バイトのunsigned intを4つセットにしたベクタ。要するに、2バイトじゃなくて4バイトのデータをくれ、ということなので、UInt32に変換してデータを渡してみると、
[SceneKit] Error: SCNSkinner: bone indices must be uint8 or uint16 (maximum of 2 bytes)
boneIndicesは最大2バイトまでだという。何を言っているのか。
SceneKitに渡せるのは2バイトまで。SceneKitの先にあるMetalに渡す必要があるのは4バイト。詰んだ。

追記:
GLTFQuickLookを作った時に偶然発見したんだけど、NSKeyedArchiverでSCNSceneをNSDataに変換した後、SCNSceneのイニシャライザにデータを渡したら、SCNSkinnerの問題を回避できた... とりあえず、バグが直るまではこれで対応しよう。

その他バグ

以前、いくつかのバグを報告をしたところ、1件だけ反応があり、最新のベータ版OSで再発するか試してくれ、と書いてあったので試したけれど、挙動が変わったものの問題は解決していなかった。報告用のログを集めるのが面倒なのと、ベータ版を使い続けるのが嫌になってダウングレードしてしまったので、返事をせず放置していたら、バグレポートがクローズされてしまった。残りのレポートは応答なし。バグを報告するのが億劫になってきた。でも、SCNSkinnerについてはなんとかしないと先に進めないので、適当に書いて送ろう。
2017/10/28(Sat) 05:39:06

 GitHubから取得したファイルをindexedDBに保存する / magicien 

ブラウザ(JavaScript)からGitHubのリポジトリのアーカイブを取得・展開するの続き。
展開したファイルをIndexedDBに保存する。


DBのオープンやデータの更新を要求すると、戻り値として IDBRequest のオブジェクトが返ってくる。このオブジェクトの onsuccess と onerror に関数を設定すると、処理が終わった時に呼び出される。ソースはこんな感じ。例によって検証していないので、typoとかバグとかあるかも。

リクエストを発行してからでないとコールバック関数が設定できないのは設計がおかしいんじゃなかろうか。
とりあえず、getResult関数でonsuccess、onerrorをPromiseでラップしておく。transactionの場合は、イベント名がちょっと違って、oncomplete、onerror、onabort になる。ディスクの空き容量が足りないと、QuotaExceededError が起きて onabort が呼び出される。手元のMacBook Airだとこのエラーが頻繁に起きる。
IndexedDBで使える容量はブラウザや状況(ディスクの空き容量)によって変わるようで、ひどい時はChromeで容量上限が10KBだった。おまえ、キャッシュとかswapとかで軽く数GB食いつぶすくせにIndexedDBには厳しすぎない?まぁ無いものは無いのでエラーを出して終了するしかない。Chromeを終了してディスクを解放してから再起動すると上限が1GBに増えていたりする。

2017/10/08(Sun) 07:46:30

 ブラウザ(JavaScript)からGitHubのリポジトリのアーカイブを取得・展開する / magicien 

需要がありそうで全く無い、ブラウザのJavaScriptで、GitHubからリポジトリをzipで落とす方法。

GitHubのAPIドキュメントによると、GET /repos/:owner/:repo/:archive_format/:ref でアーカイブがダウンロードできるらしい。
archive_formatは、zipball か tarball の二択。ref はブランチ名とかタグ名とか。リクエストを送ると、302のリダイレクトでダウンロードURLを教えてくれる(https://codeload.github.com/... とか)。

問題は、codeload.github.comがクロスドメインを許可していないこと。JavaScriptでダウンロードしようとすると、ブラウザ側で自主規制がかかって失敗する。仕方が無いので、サーバを経由してダウンロードさせることにする。サーバがGitHub Appのアクセストークンを持っているなら、プライベートなリポジトリもダウンロードできる(5分間だけ有効なダウンロードURLを教えてもらえる)ので、これが正しい方法なのだろう。

ソースコードはこんな感じ。実際に使っているものを抜粋・編集したものだけれど、未検証なので動かないかもしれない。
ここぞとばかりにasync/awaitを使ってみたが、.then.then.thenでも良いと思う。

続き:GitHubから取得したファイルをindexedDBに保存する

2017/10/06(Fri) 06:34:12

 Swift4.0でCodableなクラスを継承した場合の挙動 / magicien 

なんか想定と違ったので色々確認することにした。

結論から言うと、継承したクラスでは、init(from decoder: Decoder)を自分で実装しないとだめ。
継承元のクラスはinitを省略可。省略した場合でも、サブクラスからsuper.init(from:)を呼ぶとちゃんとパースしてくれる。

2017/10/03(Tue) 09:58:24