Swift: アウトレットを含めたプロパティで Optional “?” を積極的に使う

Swiftの特徴として、クラス名のみで示す型では、nil値を許さないことです。一方、?や!でnil値を許す変数の定義などができます。それぞれ、OptionalとUnwrapped Optionalと呼ばれています。

var myLabel1: UILabel = UILabel(frame: ...) // nilを許さないので何か入力しないといけない
var myLabel2: UILabel?
var myLabel3: UILabel!

ここで、アウトレットについて考えてみます。アウトレットは、Interface Builderで線を引くなどして参照先のオブジェクトが指定されていれば、ストーリーボードのロード時などに自動的に参照先が設定されます。しかしながら、オブジェクト生成直後では値が確定しないので、OptionalかUnwrapped Optionalのどちらかにしないといけません。

ここで、Appleのドキュメントでは、Unwrapped Optionalにすべきと書かれており、Xcodeでアシスタントエディタで、Interface Builderのオブジェクトからctrl+ドラッグでコード上にドロップしたときに作られるプロパティは、自動的にUnwrapped Optionalになります。もし、アウトレットをOptionalで定義すると、Optional Binding、つまり、

if let aLabel = myLabel2 {
    x = aLabel.text
}

のように、ifで囲う必要があります。そうなると、Objective-Cで作られたプログラムを移植するようなときに、流れが変わってしまい、作業効率が悪くなるということもあると思います。こうした点を総合して、AppleはUnwrap Optionalをアウトレットの推奨形態にしていると思われます。

しかしながら、Optionalか、Unwrap Optionalかは、仕組みの上ではどちらでも構わないのではないでしょうか。つまり、nil値を許せばいいのです。また、nil値になる状態での不要な処理を避けるという意味では、明示的にOptional Bindingを記述することになるので、不都合はなく、むしろ好都合かもしれません。余分に記述するのが目障りということもあるかもしれませんが、予防的な措置であれば、むしろ歓迎すべきです。これらの点は、個人的な嗜好ではありますが、むしろ評価すべきです。そもそも、Swfitは安全な言語ということが謳い文句だったはずです。であれば、Optionalをもっと積極的に使うべきではないでしょうか。

Optionalにしたら、面倒と思うかもしれません。たとえば、以下はいずれも、エラーになります。tagというプロパティはないとエラーでは記述されています。

myLabel2.tag = 3
let m = myLabel2.tag

しかしながら、若干の誤差を許していただければ、基本的には非常に緩いルールでこれらのエラーは逃れられます。それは、Optionalなプロパティが左辺にあるときには、単に、変数名に?をつけるだけでいいのです。右辺で利用するときには原則としてバインディングを行います。

myLabel2?.text = "aaa";
if let label = myLabel2 {
     let m = label.text
}

これだけのことでいいのです。これだけのことで、より安全になるのであれば、手間をかける価値はあるのではないでしょうか? アウトレットを含めたプロパティは、Optionalで定義する〜このルールでも構わないと考えます。

NELog.logに大量の書き込みはAdobe Creative Cloudの問題

珍しく、Adobe関連の話題ですが、いつも通りトラブルシューティングです。

ある日、OS X Yosemiteでコンソールを開くと、~/Library/LogsにあるNELog.logおよび「NELog 11-21-2014 02-58-57.log」みたいな日付つきのログが大量にあることを発見しました。しかも、毎分40KBあまりも出力している。1日50MBくらいだけど、消している時間があるので30MBくらい? 日付を見れば、何ヶ月か前からあるようで、数ギガくらい使っていそうな勢いです。しかし、なんでしょうね?このNSLog.logの正体は? 中身を見ると、こんな感じで、ちょっと手がかりがありません。SQLite関連のエラーが目に付きます。

01/12/15 03:28:07:157 | [WARN] | | ANSClient | | NELocalDataManager | | | 9983 | Not able to get the settings from local db for key (KLastNotificationResponseOlderTimestamp). Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:07:158 | [INFO] | | ANSClient | | NEGlobalData | | | 9983 | Unable to get last Notification Response Timestamp. Fetching notifications from server epoch
01/12/15 03:28:07:286 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to get the settings from local db for key (KLastNotificationResponseOlderTimestamp). Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:07:286 | [INFO] | | ANSClient | | NEGlobalData | | | 9984 | Unable to get last Notification Response Timestamp. Fetching notifications from server epoch
01/12/15 03:28:07:287 | [INFO] | | ANSClient | | NEGlobalData | | | 9984 | Mode is online
01/12/15 03:28:08:159 | [WARN] | | ANSClient | | NELocalDataManager | | | 9983 | Not able to get the settings from local db for key (KLastNotificationResponseNewerTimestamp). Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:073 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to insert the settings in local db. Error code - 0, and Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:073 | [INFO] | | ANSClient | | ConfigurationManager | | | 9984 | Unable to save parameters in Database. Default Configurations are set
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to update create new notification in local db. Error code : 0, and Error msg : Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NENotificationsProcessor | | | 9984 | Unable to create notification for notification ID: 8c1f6505-824e-43e9-8a57-15ad8f47ba7c
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NEDataFetcherAndProcessor | | | 9984 | Unable to complete Notification process operation
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to insert the settings in local db. Error code - 0, and Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NEGlobalData | | | 9984 | Unable to store last Notification Response Timestamp
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to insert the settings in local db. Error code - 0, and Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NEGlobalData | | | 9984 | Unable to store last Notification Response Timestamp
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NELocalDataManager | | | 9984 | Not able to insert the settings in local db. Error code - 0, and Error msg - Sqlite Error: The database disk image is malformed
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NEGlobalData | | | 9984 | Unable to store last Notification Response Timestamp
01/12/15 03:28:09:076 | [WARN] | | ANSClient | | NEDataRequestHandler | | | 9984 | Unable to get Notification Data from the requested timestamps
01/12/15 03:28:09:164 | [WARN] | | ANSClient | | NELocalDataManager | | | 9983 | Not able to get the settings from local db for key (KLastNotificationResponseNewerTimestamp). Error msg - Sqlite Error: The database disk image is malformed

手がかりがまるでないのですが、まず見つけたのはこのドイツ語のBBSです。ドイツ語はよくわかりませんが、これから、Adobe CCが出力することを見つけました。そして、このNELog.logを開いているプロセスをlsofでチェックして、そのプロセスIDからCreative Cloudアプリケーション、つまり、メニューバーにある無限大のみたいなアイコンのアプリケーションであることがわかります。

$ lsof | grep NELog.log
Creative   3443 msyk   66w     REG                1,4   2876129  3280800 /Users/msyk/.Trash/NELog.log
$ ps -p 3443
  PID TTY           TIME CMD
 3443 ??         0:55.59 /Applications/Utilities/Adobe Creative Cloud/ACC/Creative Cloud.app/Contents/MacOS/Creative Cloud

ここまでくると、「adobe creative cloud nelog」みたいに検索ができるわけで、そうすると、ここの先のようなメッセージがあり、そこに書いてあることをヒントにして、解決ができました。要するに設定ファイルのフォルダを作り直させれば良い模様です。手順は次のようなものです。

  1. Creative Cloudアプリケーションを終了します。メニューバーにあるアイコンをクリックしてウインドウを表示し、右上にあるギアのアイコンをクリックして表示されるメニューから「終了」を選択します。
    shot9808
  2. 以下のようにターミナルからコマンドを入力して、設定フォルダを一旦空にします。念のため、現状を別のフォルダ名にバックアップします(別にターミナルでなくてもよく、Finderで作業してもいいです)。
cd "~/Library/Application Support/Adobe"
mv OOBE OOBE_old
  1. アプリケーションフォルダにあるAdobe Creative Cloudフォルダの、Adobe Creative Cloudアプリケーションをダブルクリックして起動します。

これで大量のログ吐き出しはなくなりました。NELog.logは1分ごとに数行程度の出力をしていますが、まあ、それは無視していいくらいかと思います。なお、記憶しているAdobe IDは消えてしまうようで、その再入力は必要です。

なお、Creative Cloudは他にoobelib.log、ACC.log、PDApp.logというログもけっこう大量に吐き出しますが、まあ、NELog.logほどではありません。こちらは、アプリ起動時に大量のログを書き出します。数ヶ月で100MB程度ですが、これらについても日付のついたファイルは不要と思います。

Yosemiteのmanは2本指スライドでスクロールするぞ!!

Yosemiteを入れた方、ターミナルを起動して、たとえば、「man diskutil」と入れてください。長いmanをスクロールするのは、えーっとcontrlなんだっけ〜と思う前にトラックパッドで2本指でスライドさせてください。なんと、manがスクロールします。Linuxマシンにつないでもできますよ。

ということで、manというより、Terminal.appの動作がトラックパッドに最適化されたってことですが、わかりやすいタイトルをつけてみました。