Swift と Objective-Cの違い

 

ずっとObjective-Cで開発をしてましたが、やっとSwiftデビューしました。

そんな私が、SwiftとObjective-Cって違うなと思ったことをメモしておきます。

 

確認環境:Xcode 7.3

 

・ファイル構成

Objective-CはC、C++と同様にヘッダーファイル(.hファイル)とソースファイル(.cファイル)で構成されますが、

Swiftはヘッダーファイル(.hファイル)とソースファイル(.cファイル)に分かれていません。

(.swiftファイル)のみで構成されます。

 

・文末のセミコロン

Objective-Cは文末にセミコロンが必要ですが、

Swiftは文末にセミコロンがなくても良いです。

 

・変数の型宣言

Objective-Cは変数に対して、int とか NSString とか NSMutableArray とか型を明示的に宣言しますが、

Swiftは明示的に宣言しなくても良いです。その場合、コンパイラが適切な型を割り当てます。

もちろん、Swiftは明示的に型を宣言することもできます。

 

・オプショナル型

Swiftでは、基本的には変数や定数にnilの代入できない。

これを許可する特別なオプショナル型という型が用意されている。

 

・repeat-while文

Objective-Cのdo-while文が、

Swiftでは、repeat-while文と記述します。

 

・switch-case文

Objective-Cではswitch-case文のcaseの中にbreakを記述しますが、

Swiftでは、caseの中にbreakの記述をしなくても良い。

 

・デバッグ関数

Objective-CではNSLogでデバッグエリアのアウトプットウィンドウに表示しますが、

Swiftでは、printでデバッグエリアのアウトプットウィンドウに表示します。

 

Open Weather Map Web APIを使ってiPhoneアプリ作成

 

Open Weather Mapが提供しているWeb APIを使ってiPhoneアプリ作成してみました。

Web APIの使い方をメモしておきます。

 

確認環境:Xcode 7.3

 

有名どころのコンテンツ各社が提供しているWeb APIを活用すれば、アプリ開発の際、自分で何から何まで全部作る必要がなくなり、

さらに、Web APIを提供しているコンテンツの洗練されたノウハウを取り入れることもできます。

 

今回は、Open Weather Mapが提供している気象情報のWeb APIを使ってiPhoneアプリ作成してみます。

 

まず、はじめに、以下の Open Weather Mapのサイトにアクセスして、Sign Up をします。

 

Sign Up が完了したら、Sign Inしてください。

その後、「API」をクリックします。以下のような画面が表示されます。


Tip_01_01_009a

 

今回は、Current weather data の Subscribe を選択します。

FreeプランでAPI key を取得します。


Tip_01_01_009b

 

API Key が作成されたこと確認します。

このAPI Keyは後から使います。


Tip_01_01_009c

 

次は、Xcodeでコードを書いていきます。

Single View Applicationでプロジェクトを新規作成します。

 

プロジェクトを新規作成したら、SBJsonフレームワークをプロジェクトに取り込みます。

GitHubで公開されているものの最新版はv4.0.0ですが、

うまく使いこなせなかったので、今回はv3.2.0を使います。

Downloads の Source code (Zip)をクリックして、ダウンロードします。


Tip_01_01_009d

 

ダウンロードしたZipファイルを適当な場所で解凍します。

解凍したら、Classesフォルダのファイルを全て、Xcodeのプロジェクトにコピーします。

ルート階層に「json320」という名前でグループ(フォルダ)を作って、そこに放り込みます。


Tip_01_01_009e

 

次はStoryboardの設定をします。

Storyboardのど真ん中にラベルを1つだけ配置します。

 

このラベルを ViewController.h に「label1」という名前でアウトレットしておきます。

あとは、先ほどプロジェクトに取り込んだ、SBJsonフレームワークが使えるように、

「#import “SBJson.h”」を追加しておきます。


Tip_01_01_009f

 

最後に ViewController.m に以下のコードを書きます。

12行目の★★★の箇所には、先ほど取得した、自分のAPI Keyを記述します。

実行すると、こんな感じにラベルに東京の現在の天気が表示されます。


Tip_01_01_009g

今回はリクエストデータで東京の現在の天気としていますが、

上記のソースコードの12行目を変更すれば、名古屋や大阪などの天気を表示することもできます。

このようなリクエストデータやレスポンスデータの仕様は、以下に書かれています。

 

このようにWeb APIをうまく使えば、楽してリッチなコンテンツの作成ができます。

Web APIってほんとすごいし、おもしろいですね。

 

@propertyの属性 strong と weak

 

よくよく考えると?だった、@propertyの属性の「strong」と「weak」について

あっちこっちの資料を調べた結果、一応、こういうことなんじゃないかということを整理してみました。

間違っていたら、ごめんなさい。

 

確認環境:Xcode 7.2

 

まずは、動かして確認しましょう。

 

strong属性の「strongProperty」weak属性の「weakProperty」という2つのプロパティを持つ

「TESTClass001」クラスのクラスインターフェースを定義します。

なお、@propertyの属性は特に何も指定しなければ、「strong」になります。

 

TESTClass001.h

「viewDidLoad」で以下のような処理を行います。

 

ViewController.m

実行結果は、以下の通りです。

2回目のNSLogの結果で、「weakProperty」の値が Null になってます。


Tip_01_01_008a

 

行ごとに説明していきます。

 

12行目: TESTClass001 *testClass = [[TESTClass001 alloc]init];

「testClass」という「TESTClass001」クラスのインスタンスを生成します。

 

14行目: NSString *string = [[NSString alloc]initWithFormat:@”変更前”];

NSString型「string」オブジェクトを「alloc」して「init」します。

「alloc」すると、オブジェクトは、メモリ領域に割り当てられます。

ついでですが、割り当てたメモリ領域は、たまたまその領域に残っていた「ゴミ」の影響がないように0が埋めらます。

そして「init」で初期値 ”変更前” を設定します。

 

おそらくですが、イメージとして、メモリ領域にこんな感じで「string」オブジェクトが割り当てられます。

(アドレスは適当です。)


Tip_01_01_008b

 

ここで注目する点は、ARC(Automatic Reference Counting、自動参照カウント)では、

オブジェクトは「参照カウンタ」を持っているということです。

オブジェクトを生成すると、「参照カウンタ」は1になります。

なので、「string」オブジェクトの参照先 ”変更前”の「参照カウンタ」は、この時点で1になります。

 

16行目: testClass.strongProperty = string;

「testClass.strongProperty」に「string」オブジェクトの参照先 ”変更前” のアドレスがセットされます。

「testClass.strongProperty」は「strong」属性のプロパティです。

「strong」属性のプロパティからの参照は参照カウンタを+1します。

なので、「string」オブジェクトの参照先 ”変更前” の「参照カウンタ」は、この時点で2になります。

 

このことを「testClass.strongProperty」が ”変更前” を所有している、とか

「testClass.strongProperty」から ”変更前” への強い参照がある、と言います。


Tip_01_01_008c

 

17行目: testClass.weakProperty = testClass.strongProperty;

「testClass.weakProperty」に「testClass.strongProperty」の参照先 ”変更前” のアドレスがセットされます。

「testClass.weakProperty」は「weak」属性のプロパティです。

「weak」属性のプロパティからの参照は参照カウンタを変化させません。

なので、testClass.strongProperty」の参照先 ”変更前” の「参照カウンタ」は、この時点で2のままです。

 

このことを「testClass.weakProperty」は ”変更前” を所有していない、とか

「testClass.weakProperty」から ”変更前” への弱い参照がある、と言います。


Tip_01_01_008d

 

19行目: NSLog(@”strongProperty = %@ weakProperty = %@”, testClass.strongProperty, testClass.weakProperty);

「testClass.strongProperty」と「「testClass.weakProperty」」の値を確認します。

strongProperty = 変更前 weakProperty = 変更前

となっています。

 

22行目: string = [[NSString alloc]initWithFormat:@”変更後”];

再度、NSString型「string」オブジェクトを「alloc」して「init」します。

14行目で、「alloc」したのと別のメモリ領域にオブジェクトを生成します。

22行目で、「string」オブジェクトを新たに生成したので「string」オブジェクトの参照先 ”変更後”の「参照カウンタ」は、1になります。

一方、14行目で生成した「string」オブジェクトの参照先 ”変更前”の「参照カウンタ」は、

14行目で、生成した「string」オブジェクトの参照がなくなるので、−1され1になります。


Tip_01_01_008e

 

23行目: testClass.strongProperty = string;

「testClass.strongProperty」に「string」オブジェクトの参照先 ”変更後” のアドレスがセットされます。

「testClass.strongProperty」は「strong」属性のプロパティです。

「strong」属性のプロパティからの参照は参照カウンタを+1します。

なので、「string」オブジェクトの参照先 ”変更後” の「参照カウンタ」は、この時点で2になります。

一方、14行目で生成した「string」オブジェクトの参照先 ”変更前” の「参照カウンタ」は、

「testClass.strongProperty」からの参照がなくなるので、−1され0になります。


Tip_01_01_008f

 

25行目: NSLog(@”strongProperty = %@ weakProperty = %@”, testClass.strongProperty, testClass.weakProperty);

「testClass.strongProperty」と「「testClass.weakProperty」」の値を確認します。

strongProperty = 変更後 weakProperty = (null)

となっています。

 

ここまでの処理で、14行目で、生成した「string」オブジェクトの参照先 ”変更前”の「参照カウンタ」は、0になりました。

ARCでは、オブジェクトは「参照カウンタ」が0になると、割り当てが解放されます。

「testClass.weakProperty」からの参照が残っていますが、

「testClass.weakProperty」はweak属性のプロパティなので、

弱い参照があるだけでは、オブジェクトの寿命を保つことはできません。

なので、14行目で、生成した「string」オブジェクトの参照先 ”変更前” は解放され、その結果、「testClass.weakProperty」はnilになります。

 

補足:

このように、プログラムが動的に確保したメモリ領域のうち、不要になった領域を自動的に解放する機能のことを  ガベージコレクション(garbage collection; GC)といいます。

Objective-Cでは、ガベージコレクションとして、参照カウント方式を採用しています。

参照カウント方式には、

・MRR(Manual Retain-Release、明示的に記述したコードによる獲得と解放)

・ARC(Automatic Reference Counting、自動参照カウント)

の2つの手段があります。

「strong」と「weak」プロパティの属性は、ARCで使用する属性です。

 

delegate と protocol

 

View ControllerとTable Viewを例にして、

「delegate」と「protocol」について整理してみました。

 

確認環境:Xcode 7.2

 

ユーザーは、テーブルビューのセルをタップしたり、スクロールしたりします。

この時に発生したイベントのいくつかを、テーブルビューが処理するのではなく、

ビューコントローラーに処理を任せます。

このことを、デリゲート(delegate)と言います。

 

例として、

「TEST001TableView」Table Viewを配置した「TEST001ViewController」View Controllerがあるとします。

「TEST001TableView」Table Viewがデリゲートする側です。

「TEST001ViewController」View Controllerはデリゲートされる側です。

 

実装は、デリゲートされる側の「TEST001ViewController」View Controllerに、以下のようにします。

デリゲートする側の「TEST001TableView」Table Viewには、特に何も実装しません。

 

デリゲートする側の「TEST001TableView」クラスは、「delegate」プロパティがあります。

この「delegate」プロパティに、デリゲートされる側の「TEST001ViewController」クラスを指定します。

「TEST001ViewController.m」で実装しているので、自分自身である「self」となります。

 

TEST001ViewController.m

「TEST001ViewController.h」で

@interface の箇所に <UITableViewDelegate> と記述します。

この <UITableViewDelegate> が、デリゲートプロトコルメソッドです。

 

TEST001ViewController.h

<UITableViewDelegate> デリゲートプロトコルメソッドの中身を見てみましょう。

エディター上で、「command」キーを押しながら、<UITableViewDelegate> の箇所をクリックします。

こんな感じで「UITableViewDelegate」にどんなメソッドがあるか確認できます。

 

UITableView.h

1行目に

@protocol UITableViewDelegate < NSObject, UIScrollViewDelegate>

とありますが、

これが、「UITableViewDelegate」という名前のプロトコルであることを表しています。

 

一般に、クラスは、そのクラス固有のメソッドやプロパティを持っていて、ヘッダーファイル(.h)に、メソッドやプロパティを宣言します。

これに対して、プロトコルとは、どのクラスにも結びついていないメソッドやプロパティを定義したものです。

どのクラスにも結びついていないものなので、プロトコルを使用するには、クラスに結びつける必要があります。

この例の場合、デリゲートされる側の「TEST001ViewController」クラスのヘッダーファイル「TEST001ViewController.h」の

@interface の箇所に <UITableViewDelegate> と記述し、

「TEST001ViewController」クラスが、「UITableViewDelegate」プロトコルを使用する旨を宣言します。

 

あとは、「TEST001ViewController」クラスのソースファイル「TEST001ViewController.m」に、

「UITableViewDelegate」プロトコルで宣言されたメソッドを実装します。

この例の場合は、3行目に「@optional」と記述されています。

これは、3行目以降に宣言されたプロトコルのメソッドが、必須ではなく、任意であることを示しています。

なので、「TEST001ViewController.m」にこれらの「UITableViewDelegate」プロトコルメソッドを実装しても、しなくてもOKです。

 

なお、以下は「UITableViewDataSource」プロトコルメソッドですが、

3行目に「@required」と記述されています。

これは、12行目の「@optional」より上に記述された、以下の2つのプロトコルメソッドが、必須であることを示しています。・numberOfRowsInSection・cellForRowAtIndexPath

なので、「TEST001ViewController.m」に、この2つのプロトコルメソッドを実装しないと、コンパイラが警告を出します。

 

UITableView.h

これで、「TEST001ViewController」クラスは、「TEST001TableView」クラスからデリゲートされまた。

 

 

@propertyの属性 atomic と nonatomic

 

@propertyの属性の「atomic」と「nonatomic」について整理してみました。

 

確認環境:Xcode 7.2

 

Appleの文書には、「atomic」プロパティについて、以下のように記載されています。

なお、@propertyの属性は特に何も指定しなければ、「atomic」になります。

参考文書:

Objective-Cによるプログラミング

自動生成されたアクセサは、異なるスレッドからいくつか同時に呼び出したとしても、完全な形で値を取得/設定できる。

注意: プロパティのアトミック性は、スレッドセーフであるかどうかとは別の概念です。

あるスレッドが、アトミックなアクセサを使って、XYZPersonオブジェクトの姓と名前を変更する、

という状況を考えてみましょう。

同時に別のスレッドが姓と名前を取得しようとすれば、アトミックなゲッタは(クラッシュすることなく)完全な文字列を返しますが、

それが正しい組み合わせである保証はありません。

変更前に姓にアクセスし、変更後に名前にアクセスすると、

正しく対応していない氏名が得られることになるのです。

 

これを検証してみます。

まず、「firstProperty」という「atomic」属性のプロパティを1つ持つ「TESTClass002」クラスを作成します。

 

TESTClass002.h

「ViewController.m」の「viewDidLoad」で、

「TESTClass002」クラスのインスタンス「testClass」を作成し、

メインスレッドとバックグラウンドスレッドで「firstProperty」に文字列を追加していく処理をしてみます。

 

ViewController.m

デバッグナビゲータでは、バックグラウンドのスレッドが生成されているのを確認できます。


Tip_01_01_006b

実行結果をログで確認します。


Tip_01_01_006a

この結果をみると、メインスレッドとバックグラウンドスレッドで同時に「firstProperty」プロパティにアクセスしているのがわかります。

 

また、赤い囲みの箇所ですが、時系列で処理の流れをみると、

1.メインスレッド:処理開始「・・・back96」という文字列を取得。

2.バックグラウンドスレッド:処理開始「・・・back96」という文字列を取得。

3.メインスレッド:処理終了「・・・back96」に「99」という文字列を追加。

4.バックグラウンドスレッド:処理終了「・・・back96」に「back97」という文字列を追加。

となっていることがわかります。

注意する点は、3.でメインスレッドが追加した「99」という文字列が、書き換えられてしまっているというところです。

これは、@propertyで属性を「atomic」にしても、この点は保証されないようです。

つまり、

注意: プロパティのアトミック性は、スレッドセーフであるかどうかとは別の概念です。

ということなんでしょうか。

並列処理やマルチスレッドを使う場合、この点に注意して、プログラムを実装しないとダメですね。

 

次は、「nonatomic」の検証をしてみます。

Appleの文書には、「nonatomic」プロパティについて、以下のように記載されています。

参考文書:

Objective-Cによるプログラミング

nonatomicというプロパティ属性を指定すれば、自動生成されるアクセサは、単に値を直接取得/設定するだけのものになります。

異なるスレッドから同時にアクセスしたときの結果は保証されません。

その代わりnonatomicなプロパティはatomicなプロパティと違ってアクセス処理が高速であり、また、たとえばゲッタのみ独自に実装して組み合わせることも可能です。

 

「TESTClass002」クラスの「firstProperty」のプロパティの属性を「nonatomic」に変更します。

 

TESTClass002.h

実行してみます。

すると、半々くらいの確率で、正常に実行できたり、エラーになったりします。

正常に実行できた時のログはこんな感じです。

これは、@propertyで属性を「atomic」の時と同様です。


Tip_01_01_006c

 

エラーになった時は、こんなメッセージがでます。

ケース1 malloc: *** error for object 0x7fb861668d20: pointer being freed was not allocated

ケース2 *** -[CFString release]: message sent to deallocated instance 0x7fe821d80af0

 

つまり、

異なるスレッドから同時にアクセスしたときの結果は保証されません。

「nonatomic」なんで、クラッシュしちゃうということなんでしょうか。

 

結論としては、

・並列処理やマルチスレッドを使う場合、「atomic」か「nonatomic」を意識すること。

・複数のスレッドから同時にアクセスされる可能性があるプロパティ(インスタンス変数)は、「atomic」にする。

・さらに、同時アクセスされた時にどうするかを考えて実装する。

という感じでしょうか。

 

なお、wikiによると、「atomic」とは「不可分操作」とのこと。

「不可分操作」とは以下の2つの条件を満たさなければならないらしい。

1.全操作が完了するまで、他のプロセスはその途中の状態を観測できない。

2.一部操作が失敗したら組合せ全体が失敗し、システムの状態は不可分操作を行う前の状態に戻る。

 

All or Nothingということですね。

 

@synthesizeとは

 

@synthesizeで、オブジェクトのプロパティのインスタンス変数の名前を変更できます。

検証してみました。

 

確認環境:Xcode 7.2

 

こんな感じで「TESTClass001」クラスを作成します。

「someProperty」というプロパティと「someMethod」というメソッドを作成します。

 

TESTClass001.h

「someMethod」にこんな感じでコードを実装します。

「someProperty」に「Hello」という文字列を設定し、ログ出力させます。

 

TESTClass001.m

「ViewController.m」の「viewDidLoad」で、

「TESTClass001」クラスのインスタンス「testClass」を作成し、

「someMethod」を呼び出します。

 

ViewController.m

14行目で、ブレイクポイントで止めて、デバッグエリアで変数の値を確認します。

「testClass」クラスインスタンスの「_someProperty」の値が「Hello」となっていることを確認します。

この「_someProperty」が「someProperty」プロパティのインスタンス変数です。


Tip_01_01_005a

 

インスタンス変数とは、オブジェクトが「生きて」いる間、その値を保持するための変数です。

インスタンス変数用のメモリ領域は、オブジェクトの生成時に(allocで)確保され、消滅時に解放されます。

特に指定しなければ、このインスタンス変数の名前は、プロパティ名の先頭にアンダースコアがついたものになります。

なので、「_someProperty」がインスタンス変数名となります。

 

今度は、「@synthesize」を宣言して、インスタンス変数名を変えてみます。

「TESTClass001.h」にこんな感じで、「@synthesize」を宣言します。

 

TESTClass001.h


ブレイクポイントで止めて、デバッグエリアで変数の値を確認します。

「@synthesize」で宣言した、インスタンス変数「otherProperty」に変わっていることが確認できました。


Tip_01_01_005b

 

ここで注意する点としては、

プロパティの値を保持するインスタンス変数だけが「otherProperty」になる。ということです。

プロパティ名は「someProperty」のままであること。

アクセサメソッドも「someProperty」や「setSomeProperty」のままであること。

ドット構文でのアクセスも「someProperty」のままであること。

 

Objective-Cのアクセサメソッド

 

アクセサメソッドとは、オブジェクトのプロパティ値を取得/設定するメソッドです。

・・・と言われてもよくわからなかったので、調べました。

 

確認環境:Xcode 7.2

 

こんな感じで「someProperty」というプロパティを持つ、「TESTClass001」クラスを例とします

 

TESTClass001.h

「someProperty」プロパティのアクセサメソッドは、「setSomeProperty」と「someProperty」となります。

「setSomeProperty」がセッターメソッドでプロパティに値を設定するメソッドです。

「someProperty」がゲッターメソッドでプロパティの値を取得するメソッドです。

 

TESTClass001.m

セッターメソッドは、「set」の後に、プロパティ名の先頭を大文字にして連結した名前です。なので、この例の場合は、「setSomeProperty」となります。

ゲッターメソッドは、プロパティと同じ名前です。なので、この例の場合は、「someProperty」となります。

そういう命名規則になっているんです。

 

また、アクセサメソッド(セッター/ゲッターメソッド)の代わりにドッド構文を使ってもプロパティにアクセスできます。

これは、アクセサメソッドの呼び出しを簡潔に記述する、便宜のために提供されている機能だそうです。

でもドット構文を使ってコードを書く方が一般的?なのかな

 

TESTClass001.m

ヘッダーファイル(.hファイル)にわざわざ「setSomeProperty」「someProperty」というメソッドを書かなくてもいいんです。

ドット構文を使ってコードを書いていると、アクセサメソッドをあまり意識しませんが、こういう仕組みになってます。

 

なんで、こんなややこしいことをしているかといえば、

オブジェクト指向プログラミングは、内部に保持されている値に直接アクセスせず、

オブジェクトのプロパティには、公開されている何らかの手段(メソッド)を介してアクセスする

という、カプセル化の原則に従っているからです。

 

#pragma markとは

 

「#pragma mark」は、コメントのようなものです。

ソースコード内に区切りを入れます。

 

確認環境:Xcode 7.2

 

ソースコードの適当な箇所に「#pragma mark – 何か適当な名前」を入力します。

こんな感じで、区切りが入ります。

選択すると、ソースのその場所にJUMPします。

ソースコードが長くなった時に「#pragma mark」を使うと便利です。


Tip_01_01_003a