ラベル Swift の投稿を表示しています。 すべての投稿を表示
ラベル Swift の投稿を表示しています。 すべての投稿を表示

2015年2月26日木曜日

VRDisplay1.03をリリースしました

以前公開したVRDisplayのマイナーアップデートを行いました。
変更点は以下のとおり。

  1. ディスプレイの表示を調整し、以前よりも見やすく大きく表示するように
  2. Windowのタイトルバーを非表示に
  3. OSのメニューバーを非表示に
  4. Oculus Riftが接続されていない時のエラーメッセージを追加
  5. Mac OS 10.9における不具合により、対応OSを10.10以降に変更
以下詳細です。

1. ディスプレイの表示を調整し、以前よりも見やすく大きく表示するように
DK1に比べて、DK2の解像度が上がったとはいえ、やはりOculus Rift経由で画面を表示すると、DPIの関係上、どうしても文字がほぼ読めなくなってしまいます。そのため、以前のバージョンよりも画面を大きめにレンダリングするように変更しました。デメリットとしては、実際に巨大なスクリーンが目の前に出現するようになり、視界をほとんど覆ってしまうことですが、その分文字は読みやすくなっています。

また、Tips的には、メインのディスプレイの解像度をあえて低めに設定することで、解像度の低い小さめのディスプレイがVRDisplay内に表示され、現実的に作業がしやすくなります。

2. Windowのタイトルバーを非表示に
3. OSのメニューバーを非表示に
Oculus Riftを装着した際に、なぜか視界の上方がぼんやりと明るくなっていたので調査を行ってみたところ、WIndowのタイトルバーとOSのメニューバーの二つが表示されておりました。

この二つを非表示にすることで、先の問題も解決され、本来あるべき没入感を提供することが可能になりました。

4. Oculus Riftが接続されていない時のエラーメッセージを追加
5. Mac OS 10.9における不具合により、対応OSを10.10以降に変更
4は5の調査を行った際に副次的に追加したものですが、とりあえずそちらの方が親切なので残しました。
5に関しましては、ユーザー様より報告があり、調査をしたところ、どうやら10.9においてはNSApplicationDelegateのapplicationDidFinishLaunchingが呼び出されていないようで、一旦現状の対応端末から外しています。

原因については調査中で、何か分かり次第このブログでも報告したいと思います。

以上小さめのアップデートでした。

Gumroadで$4で販売中です。

公開時の元記事は以下
SwiftでOculus Rift DK2向け仮想デスクトップアプリを作った

2015年1月23日金曜日

SwiftでOculus Rift DK2向け仮想デスクトップアプリを作った

先日Oculus Rift DK2を買ったので、Swiftで仮想デスクトップアプリを作りました。
http://vrdisplay.tumblr.com/post/108885580424/vrdisplay-vr-desktop-application-for-mac-with

このアプリを開発するにあたって、以下の点で色々と苦労しました。
  1. Swift関数ポインタを引数に取る関数がうまく呼べない
  2. 画面キャプチャが重い
  3. 仮想ディスプレイがソフトウェアレンダリングでしか動かない
  4. ウィンドウごとのリアルタイムキャプチャが取れない
  5. 開発中にVR酔いになる
  6. DK2でも解像度が足りない
  7. Mac AppStore用にsandbox化するとOculus Riftが動かない
1. Swift関数ポインタを引数に取る関数がうまく呼べない
Swift + OpenGL + OculusSDKというかなり無理のある構成だったのですが、OpenGLのセットアップを行うときに、CADisplayLinkを用いて画面のリフレッシュに合わせてレンダリングを行うということをしようとしたのですが、
どうしてもSwiftからいい感じに関数ポインタを渡すことができませんでした。
そのため、ここは諦めて素直にObjective-Cを利用し、あとは基本Swiftという構成でなんとか乗り切りました。

OpenGLやOculusに関しては意外にもすんなりとSwiftで利用することができたので、
とにかく関数ポインタをコールバックとして渡す系が鬼門なのかなという印象です。

2. 画面キャプチャが重い
ただでさえOculusの駆動にマシンパワーが必要で、さらに3Dのレンダリングも利用しているので、画面データの転送にはなるべく軽快に済ませる必要がありました。
はじめはCGWindowListCreateImageを利用して毎フレーム画像を作っていたのですが、さすがに重すぎたために、AVCaputureScreenInputを利用して書き直しました。
この関数は毎フレームのキャプチャに向いていて、iOSのカメラのようにひたすらCMSampleBufferが届くので、それをOpenGLのテクスチャとして貼り付けてレンダリングすることで、軽快な動作を実現しました。

3. 仮想ディスプレイがソフトウェアレンダリングでしか動かない
このアプリを利用することによって、実際に存在するディスプレイの制約から解放されます。すなわち、Oculus Riftさえあれば、実際のディスプレイは必要ないということです。

なので、Kernel Extensionを利用して、Macに仮想的なディスプレイが存在していると思い込ませ、そのディスプレイのキャプチャをリアルタイムでOculus Riftで表示してあげれば、
ディスプレイは無限に増やせることになります。

実際にEWProxyFramebufferなどを利用して、仮想ディスプレイを作り出し、それらをVRDisplay上で表示するテストなどを行ってみたのですが、ハードウェアアクセラレーションが効かないようで、非常に重たく、使い物にならないというのが現状でした。

なので、今回のバージョンでは仮想ディスプレイを利用することを一旦諦め、
潔く一枚の画面が目の前に表示されるという方法にしています。

また、現在開発に利用しているMacBookProが、ディスプレイを2枚までしかサポートしていないので、
もしかしたら複数枚サポートしているMac Proなどでは、ある程度軽快に動かすことができるのかもしれません。

4.ウィンドウごとのリアルタイムキャプチャが取れない
仮想ディスプレイデバイスを作成して、仮想空間に好きなだけディスプレイを置く作戦には失敗したので、どうにかしてウィンドウのキャプチャを取り続け、それらを独自に再構成することはできないものかと調べてみました。

2で説明した、もともと利用していたCGWindowListCreateImageは、個別のWindowのキャプチャを取れる点が非常に良いのですが、その分動作が重く、毎フレーム取得しようとすると、極端にFPSが低下し、全くもって使い物になりません。

MacのWindowServerに相当するアプリを一から作ることができれば、この辺りは好き勝手にできるようですが、ドキュメントなどが一切存在しないため、不可能に近い状態です。

5.開発中にVR酔いになる
例えば先ほどのFPSが非常に低下したバージョンを実際に試すと、頭を動かした瞬間に目の前全体がガクガクし、その勢いで強烈な吐き気がこみ上げてきます。

開発の序盤のうちは、正しくレンダリングができておらず、視界の隅を気にしてしまった瞬間に気分が悪くなり、そのまま復活せずにその日の開発は終了せざるを得なくなることが多々ありました。

幸いにして段々と自分の体に耐性がつき、またアプリもまともに動くなるように連れて、このような現象にさいなまれることは減りましたが、あるゲームベンダーは開発者がVR酔いをしてしまったがために、Oculus Riftへの対応を見送ったというニュースが出るくらいなので、これはこれで非常に大きな問題であるように思います。

80年代SFホラー『Routine』のOculus Riftサポートが廃止に「現行のキットでは開発全員がVR酔いする」

6.DK2でも解像度が足りない
Oculus Rift DK2はSamsungのGalaxy Note 3のフロントパネルをそのまま利用しており、解像度は1920x1080で、片目あたり960x1080の画面が利用されています。両眼でFullHDなので、テレビであれば非常に高精細で綺麗にみえますが、虫眼鏡で拡大して目の前に置くような用途では、ドットとドットの間が目立ちます。特にプログラムを書いている時などは、フォントを大きめに設定するか、画面に近づかないとうまく文字を読むことができません。

このあたりは、ハードウェア的な進化を待つしかなさそうですが、他にもソフトウェア的に虫眼鏡を実装したり、その他拡大縮小などの便利系コマンドを用意することで、多少の改善が見込めないかと考えています。

7.Mac AppStore用にSandbox化するとOculus Riftが動かない
Mac AppStoreでアプリを公開する場合には、sandbox化と呼ばれる作業が必須項目になり、USBデバイスやカメラにアクセスするようなアプリケーションは、Entitlementsに宣言しなくてはならなくなったり、アクセスするフォルダを事前に書いておかなければなりません。

XcodeのSandbox化用のUIから、USBを利用するというチェックボックスがあったのでそのチェックを入れてみたところ、画面は正しく表示されるものの、ヘッドトラッキング/ポジショントラッキングが完全に利用できなくなってしまいました。

何かしらのキーが足りていないせいだと思われるのですが、いろいろと調べてみてもあまり情報が載っておらず、またArduinoと通信するアプリケーションを作成している記事にあるのと同様の設定をしてみても利用できないので、現状AppStoreでは正常に動作するものが公開できませんでした。

他にもUSBを利用する系のアプリをSandbox化したところ、正しく動作しないという例が存在しており、Oculus Riftを利用するアプリは基本的にはMac AppStoreで配ることができなさそうです。
Apple XCode entitlements Sandbox - つらつらと日々のメモを公開するブログ

まとめ

  • SwiftでもCのライブラリは素直に利用できる
  • Oculus Riftすごい
  • 仮想ディスプレイのハードウェアアクセラレーションは課題。Mac Proならソフトウェアレンダリングでも速いかも
  • USBデバイスを使うアプリはMac AppStoreでは基本的にリリースできない
  • より高解像度のOculusが待ち遠しい


というわけで、もし気になりましたらダウンロードしてみてください。
Gumroadで$4で販売中です。
Oculus Rift DK2の元が取れたら万々歳かなと思っています。

2014年6月8日日曜日

Swift言語の入門 (7) クラス編

さてさてお待ちかね、Swiftにおけるクラスの扱いについていろいろと見ていこうと思います。Objective-Cでは、プログラムを書く上で、必要なコンポーネントは大体クラス化されており、一部C言語由来のものなどに対して構造体を使ったり、定数のまとまり等に列挙を使う程度のイメージでしたが、Swiftではそのバランスが大きくことなりそうです。

Swift言語の入門 (2) 列挙型編やSwift言語の入門 (5) 構造体編などで説明したとおり、これら二つの要素もクラスに引けを取らない機能を備えており、かつクラスとは全く異なる特徴を兼ね備えています。

この章ではこれらの違いも確認しながら、クラスの定義から利用方法までをひととおりみていきます。

1.宣言


もっとも小さいクラスの宣言は、以下の通りです。

class Player{
}

宣言と実装のように記述を二つ書く必要はなく、ただ単にクラスキーワード、クラス名、中括弧で終わりです。

プロパティや関数を宣言するには、中括弧の中にかきます。

struct Point {
    var x:Double
    var y:Double
}

class Player{
    var position:Point = Point(x:0, y:0);
    func moveTo(p:Point){
        position = p;
    }

}

このPlayerクラスは、positionという名前の座標情報を保持するプロパティを持っており、デフォルトでは(0, 0)です。moveToという関数はプレイヤーを任意の位置に移動させる関数です。構造体のときには、mutating修飾子が必要でしたが、クラスは基本的に内部状態を変えながら動くものなので、クラスで宣言される関数にmutating修飾子をつける必要はありません。そのため、内部状態を頻繁に変えるようなものにはクラスを用いて、そうでないものには構造体を採用するというのが自然な使い方になります。

ここではpositionに初期値を設定してありますが、設定しないとコンパイルエラーになります。その理由は、現状Playerクラスはイニシャライザ(コンストラクタに相当)を持っていないためpositionが初期化されず、またpositionの型はPoint型でnilを受け付けないからです。なので、Point型の最後に「?」や「!」をつけてオプションにするか、イニシャライザの中でpositionを初期化すればコンパイルエラーは起きなくなります。

2.インスタンスの生成


クラスのインスタンスを生成するには、ただ単にクラス名の後に「()」をつけ、引数を記述します。それぞれの引数には変数名をつける必要があります。

var player = Player(position:Point(x:1, y:2))

これらの変数名は、外部変数名と呼ばれ、通常イニシャライザの内部処理を記述するときにそのまま内部変数名として使われますが、以下のように書くことで、外部変数名と内部変数名を別の文字列にすることが可能です。

init(position p:Point){
    self.position = p

}

positionが外部変数名で、pは内部変数名になります。引数に外部変数名をつけられるという仕様はSmalltalkからObjective-C、そしてSwiftへと受け継がれてきており、コードを読みやすくすると同時に、外部変数名の違いによる関数のオーバーロードを行う等、他の言語にはない独特の特徴を実現しています。

3.関数の呼び出し


クラスのインスタンスの持つ関数を呼び出すには、構造体や列挙型とおなじく、「.」のあとに関数名を書きます。

player.moveTo(Point(x:100, y:-100))

引数の外部変数名は、関数側で明示的に書かない限り、呼び出し時には指定せず、内部変数名を外部変数名として指定した場合には、コンパイルエラーになります。

4. クラスの継承


クラスを継承して新たなクラスを作成するには、以下のようにクラス名の後に「:」を書き、その後に親クラスの名前を書きます。継承はクラスのみに許されている機能で、他のクラスの機能を受け継ぎながら、新たな機能を追加し再利用するための機構です。

class RoboPlayer : Player{
    init(){
        super.init(position:Point(x:6, y:0))
    }
    
    override func moveTo(p:Point){
        //ロボはたまに言うことをきかない、とか
    }
}

子クラスは、自動的に親クラスの持つイニシャライザを継承します。また、親クラスの持つ関数をオーバーライド(上書き)したいときには、override修飾子をつける必要があります。これにより、うっかり全く同じ名前の関数を作ってしまうという問題を防いでいます。

子クラスのイニシャライザでは、親クラスのイニシャライザを呼び出す必要があります。

5.デイニシャライザ(Deinitializer)


クラスのインスタンスが解放されるときには、デイニシャライザと呼ばれる特別な関数が実行されます。デイニシャライザは引数をとらず、書き方は以下の通りです。

class DeinitialzerTest{
    deinit{
        
    }
}

総括

2014年6月5日木曜日

Swift言語の入門 (6) オプション型編

Swiftの言語仕様を読んでいくと、はじめにみつかる特徴的な仕様に、オプション型があります。オプション型とは、名前の通り、必ずしも存在する訳ではない、と言った意味合いがあり、その型そのものの値を含むか、もしくはその値が存在しないことを意味する型です。

他の言語ではNullableなどと呼ばれていることもありますが、Swiftでは全ての型にオプション型が用意されており、IntやDoubleに対しても利用することができます。

JavaではNullableというタイプアノテーションがありますが、これはプリミティブ型(値型)には対応しておらず、C#のNullableは逆に値型にのみ対応しています。Swiftではどちらの種類の型のオプション型を作ることができるため、IntやDoubleなどの型や、独自のクラスのためのオプション型を作ることができます。

1.宣言


var optionalName: String? = "John Appleseed"

オプション型の宣言は、ただ単に型の最後に「?」を追加するのみです。これをつけることにより、optionalNameにnilを代入することができるようになります。
Swiftでは、オプション型でない方にはnilを代入することができず、コンパイルエラーとなるので、逆を言えばオプション型出なければ必ず値は存在しており、nilかどうかチェックする必要は全くありません。

2.オプション型のアンラップ


オプション型からオプション型でない元の型をアンラップ(unwrap)ためには、いくつかの方法があります。

2.1 if let 構文によるアンラップ

if let name = optionalName {
    greeting = "Hello, \(name)"

}

このように、if文の条件の中にlet文を書くことで、「optionalNameが値を持っていた場合には、その値をnameという変数に代入して { ... } 内を実行する」という意味のコードがかけます。optionalNameが値を持たなかったときの処理を書きたいときには、elseの中に書くこともできます。

2.2 「!」演算子によるアンラップ

また、オプション型として宣言したけれども、nilではない値を代入した直後等のような、プログラマの目から見て明らかな場合に関しては、

greeting = "Hello, \(optionalName!)"

のように、変数の末尾に「!」をつけることで強制的に元の型に変換することができます。
ただし、この方法を利用して取り出した値がnilであった場合にはランタイムエラーが起きてしまうので、使う際には注意が必要です。しかしその分いちいちnilかどうかのチェックを行わないので、nilでないことが明らかな場合には、オーバーヘッドを少なくすることが可能です。

2.3 「?」演算子によるアンラップ

メソッドチェーン等を行う際に重宝する書き方ですが、

optionalName?.isEmpty

と書くことで、optionalNameに値があった場合は全体としてisEmptyの戻り値に、nilであった場合は全体としてnilになるという書き方もできます。

この式が評価されるときには、内部的にoptionalNameがnilかどうかを判定して、nilであればその場でnilと評価し、そうでなければそのまま計算を続けるような処理がなされます。

もともとObjective-Cではnilに対するメソッド呼び出しは無視されるという仕組みが備わっていたので、その機構とほぼ同一のものとしてみてよいでしょう。

3.暗黙的にアンラップされるオプション型


元の名はImplicitly Unwapped Optionalsですが、すなわち勝手にアンラップを行ってしまうようなオプション型、ということです。挙動としてはObjective-Cの通常の変数のように、nilも入るし値もはいる型で、nilをアンラップしてしまったときはクラッシュするという型です。挙動的にはJavaの参照型のようなイメージですが、これは何のために必要なのでしょうか。

宣言方法は、「?」のかわりに「!」を型の最後に書くだけです。

  • let assumedString: String! = "An implicitly unwrapped optional string."

使い方としての違いは、値を利用する際に今まで必要だったアンラップの処理がいらないことです。

println(assumedString)

もちろん明示的にif let文を使ってアンラップすることも可能です。

  • if let definiteString = assumedString {
  • println(definiteString)
  • }

このタイプのオプション型の場合には、「!」によるアンラップと同様に、逐一nilのチェックを行わないため、オーバーヘッドが「?」のオプション型よりも小さくなります。

これだけでは使いどころがさっぱり不明ですが、これは主にARCのリファレンスカウンタにおける循環参照の問題を処理するために導入された仕様のようで、あえて「!」にすることによりアクセスのためのオーバーヘッドを減らしつつ、初期化の際に一瞬nilの瞬間を作り出すことによって、循環参照の問題を起こさなくすることができるそうです。詳細はまた別の回にて行いたいと思います。

総括

  • オプション型は全ての型に対して作れる
  • 例外的値等を処理する際に重宝する
  • 「?」はnilだったら無視される
  • 「!」はオーバーヘッドが無いかわりにnilだとクラッシュ
いらない情報
オプション型のオプション型も作れるようで、String!?ともかけたりするようです。

Swift関連記事

2014年6月4日水曜日

Swift言語の入門 (5) 構造体編

Swift言語の構造体は、C言語の構造体からパワーアップし、クラスとも異なる新しい機構へと変化しました。クラスと少々紛らわしいですが、大きな違いは変数へ代入するときの挙動で、クラスは参照渡し、構造体は値渡しです。その特徴も相まって、一度作った構造体の値を変更する関数を書くときには、mutating修飾子を指定しなければならない等、今までに無かったルールが定められています。

1.宣言

  • struct Point {
  • var x: Double
  • var y: Double
  • func norm() -> Double {
  • return norm(x * x + y * y)
  • }
  • }

C言語の構造体とは異なり、メソッドも定義できるようになりました。これでわざわざ第一引数に構造体をとって何かするような関数を大量に生成する必要がなくなりました。

2.初期化

var p = Point(x:1, y:2);

構造体を実際に利用するには、上記のような書き方をします。メンバーの名前とコロンと値をカンマで繋げていく方式です。newなどのキーワードは特にありません。

現状このPoint構造体には、フィールドの初期値が指定されていないため、引数無しでPointを作ろうとするとエラーになります。しかしながら、構造体の宣言の際に、各フィールドに初期値をあたえておけば、引数無しでも初期値を使って構造体を初期化することが可能です。

3.フィールドへのアクセス

フィールドへのアクセスは通常通り、ドット演算子を用いて行います。

println(p.x);

また、フィールドへの代入も同様です。

p.x = 10;

構造体が値渡しである証拠に、一度別の変数に代入してしまうと、その後元の変数が書き換えられても、代入先には影響を及ぼしません。

var p = Point(x:1, y:2);
var q = p;
p.x = 10;

println("p.x = \(p.x), q.x = \(q.x)") //p.x = 10.0, q.x = 1.0


4.mutating

構造体の値を書き換える関数を書くときには、mutating修飾子をつける必要があります。
例えば、Point構造体の位置をY軸にそって反転させるflipY関数を例にとります。

func flipY() {
    x = -x //この = でエラー

}

このままではエラーが表示され、コンパイルできません。なので、funcの前のmutating修飾子をつけて再度コンパイルをします。

mutating func flipY() {
    x = -x

}

総括
  • 構造体は値渡し
  • 関数も持てる
  • 構造体を書き換える関数にはmutating修飾子
Swift関連記事

Swift言語の入門 (4) パターンマッチ編

Swiftに導入されたモダンな機能の一つに、パターンマッチがあります。これは、switch文などのなかで、変数の状態によって場合分けを行うために多く用いられます。
if文をふんだんに使えばもちろん同等な機能は実現できはするのですが、パターンマッチを使うことでよりシンプルで明確に記述することができるようになります。

1.ワイルドカードパターン

  • for _ in 1...3 {
  • // Do something three times.
  • }

たとえば、for文で何らかの処理を3回行いたいけれども、現在が何回目かどうかを気にする必要がないような場合には上記のコードを書きます。1...3は[1, 2, 3]を表し、中の点が3つなので3も含むことに注意してください。

2.値束縛パターン

  • let point = (3, 2)
  • switch point {
  • // Bind x and y to the elements of point.
  • case let (x, y):
  • println("The point is at (\(x), \(y)).")
  • }

switch分のcaseが一つしかありませんが、その中でpointの3と2をそれぞれxとyに割り当てています。

3.タプルパターン

  • let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
  • for (x, y) in points {
  • /* ... */
  • }

for...inの中で、それぞれの要素に対してパターンマッチを適用する際に利用します。
タプルの要素は全て変数である必要があります。


  • let point = (1, 2)
  • switch point {
  • case (0, 0):
  • println("(0, 0) is at the origin.")
  • case (-2...2, -2...2):
  • println("(\(point.0), \(point.1)) is near the origin.")
  • default:
  • println("The point is at (\(point.0), \(point.1)).")
  • }

数値の範囲等の条件を指定してパターンマッチを行います。switch文の中でのみ利用可能です。

  • // Overload the ~= operator to match a string with an integer
  • func ~=(pattern: String, value: Int) -> Bool {
  • return pattern == "\(value)"
  • }
  • switch point {
  • case ("0", "0"):
  • println("(0, 0) is at the origin.")
  • case ("-2...2", "-2...2"):
  • println("(\(point.0), \(point.1)) is near the origin.")
  • default:
  • println("The point is at (\(point.0), \(point.1)).")
  • }

式のマッチングオペレータは ~= で表され、これをオーバーロードすることによって、独自のマッチング機構を作り出すことができます。


総括
  • Swiftではswitch文の出番がものすごくおおい
  • パターンマッチを使って型を分解しよう
  • 式のマッチングオペレータはオーバーロードできる
Swift関連記事

2014年6月3日火曜日

Swift言語の入門 (3) コレクション編

SwiftにはArrayとDictionaryの2つのコレクション型があります。

1.Array

  • var shoppingList = ["Eggs", "Milk"]

このように、角括弧で囲い、カンマ区切りで要素を記述していきます。JSONにおける配列の記述の方式と同様です。

また、String型のArrayはArray<String>型となりジェネリクス型として表現されます。これの略記法としてString[]というJavaの配列のような書き方も認められています。

  • println("The shopping list contains \(shoppingList.count) items.")
  • if shoppingList.isEmpty {
  • println("The shopping list is empty.")
  • } else {
  • println("The shopping list is not empty.")
  • }

ArrayはcountやisEmptyといったObjective-Cでも存在していたプロパティを持っています。しかしながら、Objective-Cとの大きな違いとして、変更可能性は配列の型によらず、変数であるかもしくは定数であるかに依存します。すなわち、letで宣言された配列は変更不可で、varの場合には変更が可能です。

配列変数は変更可能であるので、たとえば

  • shoppingList.append("Flour")
  • shoppingList += ["Chocolate Spread", "Cheese", "Butter"]

などと書くことで、配列に要素を追加することができます。


各要素へのアクセスは、従来通り

  • var firstItem = shoppingList[0]

のようにして行い、範囲生成と組み合わせることで、

  • shoppingList[4...6] = ["Bananas", "Apples"]

のように配列の中の特定の範囲をごっそり入れ替えることができます。この例では、4, 5, 6番目(ドットが3つあるので6を含む)の要素を["Bananas", "Apples"]で差し替える(要素の数は一つ減る)、という処理になります。

配列はObjective-CのFast Enumerationのように

  • for item in shoppingList {
  • println(item)
  • }
と書くことで走査することができます。

配列変数を別の変数に代入するときには、基本的には参照が渡され、実体は同じものを参照することになります。ただし、配列の長さを変更するような処理を行ったときに初めて、元の参照している配列からコピーされ、新たな配列が生成されます。

2.ディクショナリ

いわゆる連想配列で、キーとなる型と値となる型の二つをペアにして保持することのできる型です。

  • var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]

宣言は上記のように行い、ジェネリクスの角括弧を用いてキーと値の型を指定します。
JSONの表記法と似ているようで違うのは、JSONの場合は括弧が[ ] では無く{ }であることくらいでしょうか。

また、型推論を利用して、ジェネリクスの型注釈を省くことも可能です。

値へのアクセスは


  • airports["LHR"] = "London"

のように[ ] の中にキーを記述することで行います。

if letの構文を利用することで、キーがあるかどうかをチェックした上で、その値に関する処理を記述することができます。

  • if let airportName = airports["DUB"] {
  • println("The name of the airport is \(airportName).")
  • } else {
  • println("That airport is not in the airports dictionary.")
  • }

ディクショナリの走査にはFast Enumeration式の方法が利用でき、タプルの形式でキーと値を束縛して処理を書きます。

  • for (airportCode, airportName) in airports {
  • println("\(airportCode): \(airportName)")
  • }

ディクショナリ変数を他の変数に代入する際には、全て参照ではなく値が渡されます。そのため、ディクショナリを代入した後に、もとのディクショナリを編集しても、代入後のディクショナリはもとのまま変化がありません。


総括

  • 配列は基本参照渡し、長さを返るタイミングでコピー
  • ディクショナリは値渡し
  • for .. in で走査