<はじめに>

およそ半年ぶりに本格的にSwift勉強しようと意気込んだが色々と記憶の彼方。
手始めにと「print」を用いてのログ出力生活。
データ値のみでなくタイムスタンプやクラス名も出力したくなり試行錯誤した結果、その使い方の多彩さに心動かされたのでネタにすることに。
(長ぇ
 
どうも!
先日、伊豆の海でウツボにちょっかい出してきましたSwiftビギナーさとこ(♂)です。
今回はタイトルにもあるとおり、ログ出力関連の処理について(漁ってみた結果を)ご紹介します!
 
さてさて、
皆さんは「print」ってご存知でしょうか?
「ん?データ突っ込んでコンソールにそのデータ値を出力するやつでしょ?なめんなばーろー。
はい、私もそんな認識でした。
しかし蓋を開けてみると「何それ!知らん!」ってのが結構あったので、ご紹介したく執筆に至りました。
 

○次回記事(2017/08/21更新)
Swift 3系、Xcode 8におけるログ出力関連機能を漁ってみた~その2~
 

<本記事の内容>

【今回の内容】
・標準出力関数の使い方(基本)
 
【次回以降の内容】
・標準出力関数の使い方(応用)
・NSLogとos_logの使い方
・さとこの3分カスタマイズ
・Xcodeのデバッガでログ出力

お昼に君臨するマヨネーズの妖精が踊り出しそうなコーナーが次回以降の本命だったりします。

 
そういえば、MarkDownではSwiftが未対応のようで…コード部分のシンタックスハイライト無しです。/(^o^)\ナンテコッタイ
対応され次第すかさず更新しますのでご容赦を。
所々で無理やり画像入れます。
 
ではでは、考察開始!
(これ違うよ!などあれば、ご指摘頂けると幸いです...。
 

<開発環境>

・Xcode Version
→8.3.3
→9.0 beta 3
・Swift
→3.1
→4

※Swift 4、Xcode 9.0 beta 3においては、明らかに挙動が異なる場合はご紹介しようと思いますが、あくまでbeta版ですので参考程度に。
 

<Swift標準出力関数>

ビルトイン関数とも呼びますね。
Swiftでは以下の3つが用意されています。

【Swiftの標準出力関数】
・print
・debugPrint
・dump

へぇ、dumpもあるんだ!
(dumpってなんぞ?は後ほど。
 
以下は、標準出力関数を漁るための出力情報になります。

【出力情報〜その1〜】:定数
0ad8a349 a5cd 462c 89a6 33e2a0cb1c67
【出力情報〜その2〜】:列挙型、クラス、構造体、プロトコル、式、関数など
59154939 d626 4b00 8a43 944ba70bba22
【出力情報〜その3〜】:オプショナル型
1ad0b6c7 31c2 490b 8313 a00f397b1c5e
まずは手始めに出力情報のその1、その2の「定数、列挙型、クラス、構造体、プロトコル、式、関数」などを出力してみます。
その後、その3「オプショナル型」を見ていきましょう。
違いを確認するために、print、debugPrint、dumpすべてで同じように出力していきます。
 

<使い方(基本)~print編~>

【キーワード】
・文字列埋め込み:「"\()"」
→文字列補完(string interpolation)という文字列リテラル内で「\()」によって囲われた値を適切な値に変換する処理機能を持つ
→"\(値)"
・オプショナル(Optional)型:「型名?」
→Int?
・暗黙的アンラップ型:「型名!」
→正式名称「Implicitly Unwrapped Optional:IUO」
→String!

オプショナル型やアンラップの詳細については以下の記事がわかりやすいので、オプショナルってなんぞ?って方は目を通しておくとよいでしょう。
どこよりも分かりやすいSwiftの"?"と"!"
より詳しく掘り下げたい方はこちらもご覧になってみるとよいと思います。
SwiftのOptional型を極める
 

【print】
49a33acd 12a2 4ffb b6dc 332ccb896897
【出力結果】
4d8021ac a797 4b8c bd88 8f2fd0560522
【print】
8059fb70 afd4 413e 8b55 a00dc35f770f
【出力結果(文字列埋め込み有無)】
5513ba46 42e2 40e8 bada 183039a9979a

ひとまず、ドバーっと出力してみました。
※実行時エラーとなるものはエラー内容をコメントアウトしたもの出力してます。
 
出力内容はうんうんそうだよねってのが多いですかね。
個人的に関数突っ込んだ時に()が出力されてて、あ、そうかVoidだったねキミはて思い出す。
 
さて、
本記事の本筋とは少しズレますが、注目すべきは「オプショナル型」でしょうか。
オプショナル型で初期値を100で宣言したintOptQuestionValueをprintの引数に設定した際に以下の図のようなワーニングが表示されます。
 
【ワーニング(Xcode 8.3.3)】
Bb1c9acb e09f 479f bff2 5f24a3a9ee42
【ワーニング(Xcode 9 beta 3)】
88b0816d df55 42af 8591 4ba729a9c007
3つほど候補が表示されてますが、上の2つはnilの可能性を潰すためのワーニングになります。

  • nilだった場合にデフォルト値を設定しとけよ
  • 強制的アンラップ忘れんな
  • 最後は明示的にキャストしてくれ

今回は予めnilでないことが保証されているし影響範囲がログ出力のみなのでどれでも良さそうですが、1つ目の対応が妥当でしょうね。
(処理する前にnilチェックしろよって話ですが。
 
ちなみにXcode 9の方がXcodeの8の時よりも見やすく修正しやすい。
(主観
 
そしてもう一箇所。
IUOを引数とするprint("\(intOptExclamationValue)")の出力結果です。
文字列埋め込み前後で出力結果が異なる様子。
その3【出力結果(文字列埋め込み有無)】のそれぞれ下から2行目ですが、
一方は100、もう一方はOptional(100)になってますね。
 
ん?あれ?文字列埋め込みの時ってアンラップされないんだっけ?!
と、過去の遺産をドヤ顔で使っていたさとこは恥ずかしくなり調査。
 
色々議論してる記事ありましたので参考までに。
Swift3のIUO(!)型から属性への変更に考えさせられたこと
Swift 3.0 をいまから学ぶ Swift Evolution ウォッチング
Swift 3 対応時にハマったString Interpolate
※この記事では深くは触れませんが機会があればご紹介します!
 
結論から言うと、
→IUOはSwift2系から3系になり言語仕様が変更され、型から属性となり廃止された様子。
(アンラップされない。
→そして文字列埋め込み時の式展開ではワーニングが表示されない。
(アンラップは?しとかんでええの?的なお節が無い。
→だから無知なさとこはドヤ顔で使ってしまった。
 
/(^o^)\

  
続いて、debugPrintさんいってみましょう。

<使い方(基本)~debugPrint編~>

【debugPrint】
8d76b4e2 b55c 4829 b6ae ecbd248ddd13
【出力結果】
1ff3baac 0d9c 4891 813d d1170fe17e4c
【debugPrint】
84832f07 a466 4f02 b3ad 9d5add1c63d2
【出力結果(文字列埋め込み有無)】
180a633a 7d9d 4668 9aa3 847816b6d283
ほとんど「print」と変わりない様子。
強いて言えば文字列埋め込み時は出力結果が「""」で囲われるとこですかね。
そしてやはりIUOの件はこちらでも同じ様子。
 
また「print」では触れませんでしたが、クロージャの動作もしっかり確認できますね。
クロージャ内の処理を実行後、自身を出力しているのが見て取れます。
"クロージャの結果 = true"と次の()の2行
  
ではdumpさん、出番です。

<使い方(基本)~dump編~>

【dump】
12e303e3 0e61 45fb b1c3 3a3c88143639
【出力結果】
1022f699 4dc0 4b88 b1b2 88a1a1c96957
【dump(文字列埋め込み有無)】
1007fad5 bbce 4bf9 8762 b9eaa99d805c
【出力結果】
3d3c72c8 0a35 4f3d 860e d233fb6aa809

「print」、「debugPrint」と異なる点は2つですかね。
1. 出力時に「-」でインデントされる
2. 構造情報が出力される
 
2に関しては、出力対象のオプショナル型が列挙型(enum)であるため、その構造を値と共に出力してくれた訳です。

// Optionalの構造
enum Optional<T> {
    case None
    case Some(T)
}

今回の場合はSome(T)に該当していますね。
 
また、配列や辞書、画面サイズ(self.view.frame)等、構造情報出力時はdumpさん優秀ですね。
(辞書に関しては「print」の方が[キー:値]になってて見やすいですが。
 
そしてオマケになります。

// 引数を指定しない場合
print() // \n
debugPrint() // \n
dump() // エラー

// 関数を引数にできるということなので…
// 出力結果
print(debugPrint(dump("dump")))
- "dump"
"dump"
()

debugPrint(dump(print("print")))
print
- (0 elements)
()

dump(print(debugPrint("debugPrint")))
"debugPrint"
()
- (0 elements)

引数を指定しない場合、「print」と「debugPrint」は0個以上の引数なので問題ないですが、dumpは最低1つ以上の引数を要するようです。
e.g.)dump(1)、dump("") // これだけでもOK
 
最後は完全に遊んでますが、こんなことも可能ですという要らぬ紹介。
 

<まとめ>

【print】

  • 型を気にせず出力可能であるため、値の確認が容易
  • 文字列埋め込みをしてもしなくても同様の出力結果

【debugPrint】

  • 「print」と同等の機能を持つ
  • 文字列の出力結果が「"」で囲われる
  • 文字列埋め込みでの出力結果も「"」で囲われる

【dump】

  • Mirrorが持つ機能のように、「print」、「debugPrint」よりも詳細な情報を出力可能
  • 最低1つ以上の引数を必須とする
  • 文字列埋め込みで出力結果が「"」で囲われる

 

<おわりに>

次回は「応用編」をご紹介します。
本記事よりはボリューミーになるのでちゃんとお腹すかせといてください。
 
余談ではありますが、ログってそもそも何で必要なの?とか、
その辺気になった方はコチラを見ると良いかもですね。
 
以上!

Shere
  • はてなブログ
  • Twitter
  • Facebook
Swift 3系、Xcode 8におけるログ出力関連機能を漁ってみた〜その1〜

Writer

  • Name

    さとこ

  • Position

    どこにもあと2歩くらい足りないシャイなエンジニア

  • Profile

    C/C++/C#/Objective-C/Swift/Java/PHP,Install Shiled Script言語/Oracle DB等。 触りましましたよ、ちょんちょんって。 Swiftなう。みなさんアプリ作りましょ!