Xcode Build Settings の操作をマスターする

 Xcode Build Settings 画面は, 言われなければ気づかない操作方法があるので, それらを紹介したいと思います. これらの操作は, たくさんのターゲットを管理するような場面でとても時間の短縮になります.
 Xcode 7.2 時点の内容ですが, Xcode 4 から操作方法は同じだったと思います.

Build Settingsのヘルプを確認する

 Build Settings の項目を単一選択すると, 右の Quick Help から設定の内容を調べることができます.

Xcode Build Settings Quick Help
Xcode Build Settings Quick Help

Build Settings の定義を生の定義と値で見る

 Build Settings はデフォルトで人間が見やすい名前で書かれていますが, 正しく Build Settings を確認したいときには直接の名前を見たいです。そういう時はメニューから Editor – Show Setting Name 及び Show Settings Definitionにします.

Show raw settings definitions and values
Show raw settings definitions and values

このように Base SDK の Latest OS が SDKROOT の macosx 表記で確認できます.

設定を削除する

 設定項目を選択し, Delete キーで設定値を削除できます. ターゲットの設定値ではなくてプロジェクトの設定値を使いたいときなど, 前のレベルの設定値をそのまま引き継ぎたいときには Delete で余計な設定は削除しておきましょう.

Delete Xcode Build Settings
Delete Xcode Build Settings

ここで Delete キーを押すと, ターゲット Settings の Build Architecture Only の設定値が削除され, 1つ前のレベルのプロジェクト Settings の値が引き継がれる状態になります.

複数選択する

Build Settings の項目画面で command, shift キーを押しながらクリックすると複数選択できます. また, command + a で全選択することもできます. さらに, 左側に表示されているターゲットも複数選択が可能です.

Xcode Build Settings Multi selection
Xcode Build Settings Multi selection

 例えば, このまま Delete を押すと, 選択されたターゲットの選択されたBuild Settingsをすべて削除できます.

設定値をコピーペーストする

Build Settings を選択した状態で command + c でコピーすると, 選択された Build Settings をコピーすることができます. コピーした設定値は, 別のターゲットやプロジェクトに移動して command + v でペーストできます. コピーした Build Settings と同じ設定値がペーストされます.

 また, コピーした Build Settings は通常のテキストにペーストすることができます. 例えば Architectures の部分を選択してペーストするとこのようになります.

Xcode Build Settings Architectures
Xcode Build Settings Architectures

 設定値は自動的に configuration ごとに整理されてペーストされ, さらに設定値がなかった項目については some の所にまとめて定義のみ書き出されます.
 ご存知の通り, これは xcconfig ファイルの記述そのものです. Build Setitngs を xcconfig に移動するときは, わざわざ自分で書かずともコピーペーストだけで済みます.

Build Settings を評価する

 xcodebuild コマンドからターゲットの設定を評価することができます. 自動的に設定値をチェックするツールとか作るときに使えるかもしれません.

Build Settings の $(inherited) を正しく理解する

Xcode Build Settings に使用できる $(inherited) は, 前のレベルの Build Settings を継承するという解釈になります.

 例えば, xcconfig で #include した場合に $(inherited) を使っても意図しない記述になります. Base.xcconfig と Target.xcconfig があり, Target.xcconfig をターゲットに設定したとき

HEADER_SEARCH_PATHS は ./HeaderB ./HeaderA と解釈されるわけではなく, ./HeaderB $(inherited) と解釈されます. 同じレベルでビルド設定値を2回記述したことになり, 後者で上書きされることになるからです. このときの $(inherited) には前のレベルであるプロジェクトの設定値が入ることになります.

たくさんのターゲットを管理するには

 Build Settings は非常に重要な設定で, 内容によっては少しでも間違うと事故が起きるような設定もあります. 例えば, Deployment Target を適切に設定しなかった場合, 古い OS でアプリを動作させると強制終了するようなこともあります.

 大量のターゲットやプロジェクトのすべての Build Settings を確認することは不可能に近いので, ターゲットが増えてきたら早々に xcconfig に定義を移して, 極力 Xcode Build Settings では設定値を上書きしないという運用が効率的に思います.

iOS向け ./configure でのビルド方法

 Autotools ビルドシステムにより配布される C/C++ ライブラリは ./configure && make install の手順でビルドして使用しますが, 普通にこれを実行すると OSX 向けのビルドがされます. iOS の場合にはどのようにすればよいかを説明します.

全体の手順

  1. iOSシミュレーター向けにビルド
  2. iOSデバイス向けにビルド
  3. ライブラリを使用するXcodeの設定

 iOS の開発では iOSシミュレーター上またはiOSデバイス上で動作させるようにするため, それぞれに向けた SDK でライブラリをビルド必要があります. iOSシミュレータ上のアーキテクチャは Mac と同じi386, x86 の二種類で, iOSデバイス上のアーキテクチャは arm7, arm7s, arm64 の三種類でビルドをします. 両方のライブラリをビルドしたのち, それらを利用するプロジェクトで設定をします.

iOSシミュレーター向けビルド

 ./configure、make installの流れでビルドをします. install ではなく make でも構いませんが, 個人的には install をして成果物をまとめてくれた方が好みです. dylib のインストールパスは後で install_name_tool で付け直すので問題ありません.

 CC はCコンパイラ CXX は C++ コンパイラの指定, CFLAGS は C コンパイラ, CXXFLAGS は C++ コンパライのオプションフラグになります. -isysroot はビルドに使用する SDK の指定, -mios-simulator-version-min は Xcode Build Settings でいうと Deployment Target の指定になります.

–prefix でインストールディレクトリを与えた場合には, 指定のディレクトリをあらかじめ作成しておきます.

 ビルドが成功すると –prefix で指定したディレクトリにビルドしたものがコピーされます.

iOSデバイス向けビルド

 iOSシミュレーター向けビルドと同じ手順ですが, ./configureに与えるオプションが異なります. また –prefix を与える場合には上書きされないようにiOSシミュレーターの時とは別のディレクトリを指定します.

–host オプションが追加され, アーキテクチャと SDK がシミュレーターと異なることに注意します.

 ビルドが成功すると –prefix で指定したディレクトリにビルドしたものがコピーされます.

インストールパスの変更

 ダイナミックライブラリ dylib のインストールパスは, ./configure に与えた –prefix のディレクトリのインストールパスが書き込まれます. 通常 iOS アプリのバンドルパッケージの Framework フォルダに入れて使用するのでインストールパスの変更が必要です.

ライブラリの利用

 これらで生成したライブラリを使用するプロジェクトは, Build Settings の Header Search Path にヘッダーのディレクトリを指定, Library Search Path に .a, .dylib のディレクトリを指定, そして Build Phase の Link with Binary に .a, .dylib などを追加します.
 ライブラリの .a, .dylib は iOSシミュレーターとiOSデバイス向けそれぞれのビルドのものを指定するので, Build Settings に Add Conditional Settings を追加して, それぞれの SDK ビルド時に使用するディレクトリを分けることにより, 適切なライブラリが選択されるように設定します.
lib

おまけ

ライブラリの統合

 iOSシミュレーター向けとiOSデバイス向けのライブラリを lipo コマンドで1ファイルに統合することができます. リリース時には必要ないことですが, デバッグで使用するときなど1ファイルで取り扱えるようになるので楽になることがあります.

Objective-C インスタンス変数の C++ オブジェクト

 どうして C++ Advent Calendar 2015の11日目の記事なのに Objective-C なのか, 別の言語の記事では無いのか? いいえ, Objective-C++ は C++ を完全に含んだ言語なので, Objective-C++ は C++ でもあるのです. 互いの言語のコードを自由に混ぜて記述することができますが, それがどのような意味を持つのかは実際に試してみないとわからない部分が多いです(仕様書の存在を知りたい). そこで今回は Objective-C クラスのインスタンス変数に C++ クラス型のオブジェクトを定義したときの振る舞いについて調べてみます.

 振る舞いを調べるために次のコードを用意しました. C++ クラス C1 はコンストラクタ, デストラクタそして仮想関数 f を定義し, C2 は C1 を継承して仮想関数 f をオーバーライドします. Objective-C クラス O1 は C++ クラス C2 型のインスタンス変数 _c2 と, メソッド f を持ちます.

 このコードを実行した結果です. ( Apple LLVM 7.0 )

 期待した振る舞いをしていると思いますが, 実は昔の Objective-C++ はこのコードをコンパイルすることはできませんでした. なぜなら仮想関数を持つ C++ オブジェクトをインスタンス変数として宣言することはエラーだったためです. またその当時は, ユーザー定義のコンストラクタとデストラクタが呼び出されることはありません. このことは下記ドキュメントに記されています.

Using C++ With Objective-C – Object Oriented Programming and the Objective-C Programming Language 1.0

 しかしながら Objective-C 2.0 となってコンパイラも GCC から Clang に変化して時代が移り変わっている間に, 状況が随分と変わったようですが, この点について語られている Apple のドキュメントは無いように思います. ( Apple Developer で C++ を検索すると C になってしまってつらい )

 注目すべき点をいくつか見ていきましょう.

コンストラクタ

Objective-C クラスの C++ オブジェクトで宣言されたインスタンス変数のコンストラクタは引数無しのコンストラクタが呼び出されます. Objective-C の構文的に C++ のコンストラクタに引数を指定する方法は無いので, 引数無しのコンストラクタしか使えません.
 注目すべき点として, Objective-C の init よりも先にコンストラクタが呼び出される点です. この順番でなければ困りますが, 実際に動作を確認できると安心できます.

デストラクタ

Objective-C クラスの C++ オブジェクトで宣言されたインスタンス変数のデストラクタは Objective-C クラスが解放されるときに呼び出されています. 注目すべき点としてはやはり呼び出し順序で, Objective-C の dealloc よりも後にデストラクタが呼び出されています.

仮想関数の呼び出し

 Using C++ With Objective-C によると, 仮想関数がエラーになるとのことで心配でしたが, 出力結果の通り適切なメンバ関数呼び出しがされ, Objective-C インスタンス変数で宣言された C++ オブジェクトが期待通りに初期化されて振舞っていることがわかります.

.cxx_construct, .cxx_destruct の存在

 今回まとめるにあたり, いろいろ検索する中で 3.6 Options Controlling Objective-C and Objective-C++ Dialects のページを見つけコンパイラオプション -fobjc-call-cxx-cdtors の存在を知りました. Clang には無さそうでしたが, このオプションを外した状態にしたときに, ユーザー定義のコンストラクタ, デストラクタが呼び出されず, 仮想関数を持つオブジェクトをサポートしない状態になるようです.
 さらにこのページの情報に基づけば, C++ のコンストラクタ及びデストラクタは .cxx_construct, .cxx_destruct メソッドをコンパイラが生成して Objective-C ランタイムが適切なタイミングでそれらを呼び出すことによって実行されるとのことでした.

 コンパイラは異なりますが, 実際に O1 クラスのメソッドをリストしてみると, 確かにそのようなメソッドの存在が明らかになりました.

出力結果.

まとめ

 Objective-C++ を使用する機会を持つことはほとんどないと思われますが, 過去の C++ 資産を活かすような場面に活躍する場合があります. Objective-C++ というと, 最初に述べたと通りに Objective-C と C++ を自由に混ぜて記述することができますが, それは構文的な話であって, 実際に思った通りに動作するかといえば仕様や動作を確認する必要があります.
 Objective-C 2.0 となり, 新しい機能がたくさんできましたが, 幸いにも Blocks (厳密にはCの機能) と Automatic Reference Counting については LLVM に詳細なドキュメントがあり, もちろん C++ のことも十分に考慮されています. どちらも寿命に関わる話なので, 本格的に Objective-C++ を使用するときには, どちらともに確認しておきたいドキュメントです.

Language Specification for Blocks
Objective-C Automatic Reference Counting (ARC)

UnicodeName.app – 文字のユニコード名を表示するアプリ

 文字の Unicode 名を表示するアプリケーションを作成しました。

unicodename screenshot
unicodename screenshot

動作環境

 Mac OS X 10.9+

インストール

 UnicodeName.app をアプリケーションフォルダーにコピーします

ダウンロード

 UnicodeName1_0_0

使い方

  • 入力フィードに文字を入力すると, 表に UnicodeName が表示されます.
  • テキストを選択し, サービスメニューから [Look Up In UnicodeName] を選択します.

文字の Unicode 名とは

http://unicode.org/Public/8.0.0/ucd/NamesList.html
このアプリケーションは UnicodeData.txt を元に表示します.

ソースコード

 https://github.com/lifeaether/UnicodeName

背景

 この頃よく Emoji を見かけるようになりました。その Emoji は何を表しているか気にしてしまうので, すぐに調べられるようにと作りました.

今後の展開

  • NamesList.txt を使うべきだったかもしれない
  • 入力フィールドと表の selection の同期

エンディアンを考慮したバイト列の読み込み

適当なバイトストリームから読み込む時, 1バイトよりも大きい型はリトルエンディアンまたはビッグエンディアンで並ばれてエンコードされています. こういうときに, たとえば4バイトの型をストリームから読み込む時には, エンディアンを考慮して必要であればバイト列を逆順にしなければなりません. つまり, 読み込むバイトストリームとエンディアンが異なる場合は, バイト列を逆順で読み込むという処理をします.

このようなときの読み込み方は様々あると思いますが, 最近はこのように書いています.

どのように記述すると良いのでしょうか.

const 参照引数が const にならないとき

よくある話だと思いますが, const を付けたからといって必ずしも const が保証されるわけではありません.
例えば, b を2乗した値を a で受け取る奇妙な関数 f を考えます.

この関数を f を次のように呼び出します.

1 が出力され, 期待された結果が得られません.

このような単純なものであれば, すぐに引数 a と b が同一の参照である可能性を考慮し, なんらかの対策をして関数 f を実装できますが, 引数の数が増えたりして複雑な関数になってくるといつでも参照の同一を考慮できるとは限りません.

この問題に対する有効な対策は何が考えられるでしょうか。

C++ out 引数で結果を受け取ると const できない

out 引数で関数を受け取った場合、呼び出し側で const にできないデメリットがあります。構造体だからといってわざわざ戻り値にの代わりに out 引数で関数を受け取るような時代は終わったと思っていますが、複数の戻り値を返したいなどといったときに見かけることもあります。

基本的に関数の結果は戻り値で返してほしいものです。
この場合であればstd::pairでなんとかなります。

C言語 配列の要素を列挙したい

C言語で固定長で定義された配列の要素を列挙するときは、どのような記述で列挙するのが良いでしょうか。

たとえば、長さを sizeof を使って計算して for で反復するというやり方があります。

実際、いつもこの記述をよく使うのですが、sizeof で計算して長さを求めているところと、そもそも列挙したいのにインデックスを使ってアクセスしている点があまり気に入らないです。

Find Implicit Dependencies (Xcode)

Xcode の Scheme の項目をよく見ていると、Build のところに Find Implicit Dependencies というチェック項目があります。この項目はデフォルトでチェックが入った状態になっています。また、Scheme なので共有していない限りは、プロジェクトの設定というわけではなくて、あくまでユーザー環境の設定となります。

Find Implicit Dependencies

Find Implicit Dependencies にチェックが入っていると、システムのフレームワークは明示的に Build Phase の Link with Libraries に追加する必要はなくなり、また Link Binary With Library に追加した他の Target のライブラリ等は Target Dependencies に設定する必要なく、自動的に依存ライブラリとしてビルドされるようになります。

個人的な感触としては、依存関係を明示的にしておきたいので、 Find Implicit Dependencies は使いないようにして、 Target Dependencies に Target を追加しておきたいです。

どこかで Find Implicit Dependencies を使わない状態であると、 Run をしたとき速くなるとかいう記述を見た気がしましたが、自分の使っている範囲では特に変わらない気がします。Run をしたときに待ちたくない時には、 Run Without Building を使うので良いと思いました。

キャッシュとoptional

ある処理に時間がかかるとき、その計算結果をキャッシュしておきたくなりますが、どのようにキャッシュを作れば良いでしょうか。

この頃はoptionalを使って書いています。