Swiftに於ける変数の文字列化の評価の違い

プログラムをしていれば思わぬ結果に右往左往するのも多くあります。 其れはiPhoneアプリ開発に於いても例外ではありません。 以前では其の開発言語は Objective-C の一択であり自らもアプリを幾つか当該言語で以て記述してリリースしました。 今は多くアップル社から提供された新開発言語 Swift で書かれるアプリも多くなってきました。

開発アプリのヘックス画面

Swiftは型に厳格な言語で更には思わぬバグを防ぐために 何の値をも持たない nil (null)を許す際には当該事項も型として用意されるものです。 optional (オプショナル)型と称すものですが なかなか理解が難しく正直自身も恥ずかしながら正確な部分迄把握は出来ておらず 都度都度対処療法を施している様な状況です。

さて今回落とし穴に陥ったのは変数の文字列化に於いてでした。 既にリリースのなっているアプリをバージョンアップしようと機能追加するに於いては 変数の宣言も形や場所を変えざるを得なくなります。 従来は変数の宣言を3次元配列から一義に代入していました。 以下の如くです。

スポンサーリンク
日付:2017年4月14日
開発機:MacBook Pro (13-inch, Late 2016, Four Thunderbolt 3 Ports)
MacOSバージョン:macOS Sierra 10.12.4
Xcodeバージョン:8.3.1
言語:Swift 3.1
主関連アプリ:Fine Kingdom(邦題:楽小王国)
var eAttackId:Int = eAttackAreaArray[0][0][0]

新バージョンに於ける機能追加では此の部分を 後からif文で分岐した内に決定する必要が生じ 従って当該部分はブロック内に入ってしまいますので 取り敢えずの変数の宣言はオプショナル型を適用し以下の如き一文としました。

var eAttackId:Int!

此の様に変えたら文字列化の際に評価がされなくなってしまったのです。 評価をする部分は以下になります。 ゲームに於いてはヘックスの色を変えようと言う思惑を持つ部分に当たり、 しかし今迄此れで以て滞りなく変じていた色に変化がなくなってしまったのです。

self.childNode(withName: "hex" + "\(eAttackId)")?.run(hexGreenColorChangeAction)

そして様々力技に試行錯誤して目論見通りにヘックスを変色せしめた一文が以下になります。

self.childNode(withName: "hex" + String(eAttackId))?.run(hexGreenColorChangeAction)

どうやらString()メソッドを用いて整数型を文字列型にキャストすれば 例えば数字を**と表現するとして思惑通り hex** と言う文字列が得られ其れはプログラム中に存在するインスタンス名であるのです。

以て恐らくはオプショナル型であるのが問題ではないかと推測しました。 其処で実験をして見ます。 実験と言っても簡単に文字列化当該部分のみをログに出力して見るものです。 以下が実験と右に矢印で示したのが結果出力になります。 整数型変数eAttackIdには整数38が代入されているものとします。

print("\(eAttackId)") // => Optional(38)
print(String(eAttackId)) // => 38

上はオプショナル型が保持され、 下は思惑する所の文字列型にキャストされています。 上の状態ではインスタンス名を拾えなくなるのも宜成る哉と言うものです。

楽小王国イメージ:CaptureTheArea

以上にて後者でキャストすれば実質問題は無くなりますが 今後の開発の糧とするためにもう少し詳しく調べてみます。 以て変数の型を調べてみます。 なおSwiftのバージョンが3に上がってクラス名の出力に用いるメソッドが dynamicType から type(of:) に変更になったので注意が必要でしょう。 後者メソッドを用いて文字列化2例のクラスをログに出力してみたのが以下です。

print(type(of: "\(eAttackId)")) // => String
print(type(of: String(eAttackId))) // => String

以上クラス名は等しく String クラスであって変わりありません。 ではオプショナル型とは如何なるものか問うて見れば以下の情報が2014年12月2日付けで Qiita に共有されていました。

[Swift] Optional 型についてのまとめ Ver2

オプショナル型については wrapunwrap の概念がある、とのくだり が今回のケースには深く関係しそうです。 下に該当部分を引用します。

ラップ(wrap)とアンラップ(unwrap)

「ラップされている」とは?
Optional 型(Optional 型)の変数のことを「ラップされている」と言う (おそらく、Optional型に T 型の変数が「包まれている」ことに由来すると思われる)。

「アンラップする」とは?
Optional 型(Optional 型)から T 型の変数を取り出すことを、「アンラップする」と言う。

上記を踏まえてアンラップの実験をしてみます。 此の時、変数eAttackIdの値は整数32です。

print("\(eAttackId)") // => Optional(32)
print(String(eAttackId)) // => 32
// 前者をunwrapして表示
print("\(eAttackId!)") // => 32

従ってラップされた変数をアンラップして後文字列化すれば 今回の問題は惹起されなかったものです。 因みに以下の形を取ると Cannot force unwrap value of non-optional type 'String' とエラーが吐かれビルド出来ず本記事の全体的に整合性が取れのではないかと思うものです。

print("\(eAttackId)"!)
Fine Kingdom(邦題: 楽小王国)
無料:カテゴリ: ゲーム: 9+ 評価
バージョン: 8.0
リリース: 2017年2月7日
更新: 2020年4月9日
サイズ: 9.3 MB
互換性: iOS 12.0 以降のiPhone、iPod touch に対応。および、macOS 11.0以降とApple M1 チップを搭載したMac に対応。