SwiftとObjective-Cとの違い

2017年6月27日(火)
大津 真
この連載では、Objective-Cの経験者がスムーズにSwiftへ移行できるよう解説する。第1回目は、SwiftとObjective-Cの違いを見てみよう。

はじめに

Swiftは、macやiOS向けアプリケーション開発のための、Appleが提供する標準開発言語です。本連載では、それ以前の標準開発言語であったObjective-Cプログラミングの経験者のために、移行に当たってつまずきやすいポイントを中心に、Swift言語によるプログライミングの基礎について解説していきます。本稿執筆時点でのSwiftの最新バージョンは3.1(Xcode 8.3)ですが、今秋にリリース予定のバージョン4にも対応していきます。

読者の中には、Swiftが発表された頃に多少なりとも触れたことがある方もいると思うので、Swift 3.xでの相違点などもできるだけ織り込んでいくつもりです。もちろんObjective-CとSwiftの比較部分以外は、JavaScriptなどの他の言語の経験者にも読み進めていただけると思います。

Swiftの変遷

「モダン」「安全」「高速」「インタラクティブ」をキーワードとして、2014のWWDC(Worldwide Developers Conference)で華々しく登場したSwiftですが、Objective-Cからの移行は思ったほど進んではいないようです。プログラミング言語の人気度の目安となるPYPL(http://pypl.github.io/PYPL.html)をみても、2017年4月の時点ではObjective-Cが8位なのに対して、Swiftは10位とObjective-Cより下位に位置しています。その大きな理由の一つが、Swiftの進化のスピードの速さにあると言えるでしょう。

Swiftの進化

年月内容
2014年6月WWDCにて発表、ベータ版公開
2014年9月Swift 1.0(Xcode 6.0)リリース
2015年9月Swift 2.0(Xcode 7.0)リリース
2015年12月オープンソースとして公開
2016年3月Swift 2.2(Xcode 7.2〕リリース。
Ubuntu Linuxで動作するバイナリーも提供
2016年9月Swift 3.0(Xcode 8.0)リリース

Swiftはまだ若い未成熟な言語のため、バージョンアップごとに仕様にドラスティックな変更が加えられ、下位互換性が大胆に切り捨てられてきました。そのため、移行のタイミングをなかなか見極められないでいたObjective-Cユーザも少なくなかったと思います。

さて、これ以降もSwiftの進化は続いていく予定ですが、今後のリリースでは互換性の維持に取り組むことがアナウンスされています。ようやく、Swiftによるアプリケーション開発に安心して取り組める環境が整いつつあると言えるのではないでしょうか?

Objective-CとSwiftのソースを見比べてみよう

まずは、Objective-CとSwiftのソースプログラムを見比べながら、その基本的な相違点を説明していくことにしましょう。「モダン」と言われるSwiftのコードの雰囲気を少しでも感じ取っていただければと思います。例として、現在の日付時刻を以下のような形式でコンソールに表示する、コマンドラインツールのプログラムを取り上げます。

2017年4月18日 14時3分

Objective-Cのプログラム

まず、Objective-Cのソースを示します。NSDateオブジェクト、NSCalendarオブジェクト、NSDateComponentsオブジェクトを組み合わせて、現在時刻から年、月、日、時、分の値を取り出し、NSLog()関数で表示しています。

リスト1:日付と時刻を表示(Objective-C)

#import <Foundation/Foundation.h> // ←(1)

int main(int argc, const char * argv[])
{
    // 現在の日付時刻のDateオブジェクトを生成
    NSDate *now = [[NSDate alloc]init]; // ←(2)
    // グレゴリオ暦のカレンダーを生成
    NSCalendar *gCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    // 取り出すコンポーネントを設定する
    NSUInteger flags = NSYearCalendarUnit | NSMonthCalendarUnit
    | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit; // ←(3)
    // 現在時刻のDateComponetsを取り出す
    NSDateComponents *dComps = [gCal components:flags fromDate:now]; // ←(4)
    // 年、月、日、時、分を表示する
    NSLog(@"%ld年%ld月%ld日 %ld時%ld分", [dComps year], [dComps month],
        [dComps day], [dComps hour], [dComps minute]); // ←(5)
    return 0;
}

Swiftのプログラム

次に、同じ処理をSwiftで記述した例を示します。Objective-CとSwiftのそれぞれのソースの対応するステートメントに同じ番号を振っていますので参考にしてください。

リスト2:日付と時刻を表示(Swift)

import Foundation // ←(1)

// 現在の日付時刻のDateオブジェクトを生成
let now = Date() // ←(2)
// グレゴリオ暦のカレンダーを生成
let gCal = Calendar(identifier: .gregorian)
// 取り出すコンポーネントを設定する
let flags: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute] // ←(3)
// 現在時刻のDateComponetsを取り出す
var dComp = gCal.dateComponents(flags, from: now) // ←(4)
// 年、月、日、時、分を表示する
print("\(dComp.year!)年\(dComp.month!)月\(dComp.day!)日 \(dComp.hour!)時\(dComp.minute!)分") // ←(5)

以下、Objective-CとSwiftの違いを見ていきましょう。

フレームワークのインポート

Objective-Cの場合、NSString(文字列)、および、NSArray(配列)やNSDictionary(辞書)などのコレクションは、Foundationフレームワークに用意されていました。それに対してSwiftではString(文字列)、Array(配列)、Dictionary(辞書)などのコレクションは、Swift Standard Libraryとして言語仕様に含まれています。ただし、多くの基本的なデータ型(サンプルではDateやCalendarなど)は、Foundationフレームワークに用意されているので、インポートする必要があります。

Objective-Cの場合、プリプロセッサ命令である「#import」でヘッダファイルをインポートしました。それに対してSwiftではヘッダファイルは不要で、import文でフレームワークをインポートします(1)。

Objective-C

リスト3:プリプロセッサ命令#importでインポート

#import <Foundation/Foundation.h>

Swift

リスト4:import文でフレームワークをインポート

import Foundation

main()関数がない

C言語由来のObjective-Cの場合、最初に呼び出されるmain()関数に処理を記述する必要がありましたが、Swiftにはmain()関数は不要です。JavaScriptなどのスクリプト言語と同様に、メインとなるソースファイル「main.swift」の先頭から処理を記述します。

ステートメントの終わりは改行

Objective-Cで必要だったステートメントの終わりのセミコロン「;」は、Swiftには不要です。ステートメントの終わりは改行で判断されます。文末にセミコロン「;」を記述することも可能ですが、記述しないのがSwiftの流儀と考えて良いでしょう。ただし1行に複数のステートメントを記述する場合には、セミコロン「;」で区切る必要があります。

リスト5:1行に複数ステートメントを記述する時は「;」で区切る

print("Hello"); print("World")

オブジェクトの生成

Objective-Cではオブジェクトの生成をalloc()メソッドとinit()メソッドの組み合わせで行っていました。それに対してSwiftでは「型名(引数)」のようなシンプルな形式のイニシャライザを使用します(2)。

Objective-C

リスト6:オブジェクトの生成

NSDate *now = [[NSDate alloc]init];

Swift

リスト7:オブジェクトの生成

let now = Date()

一部で不評だった入れ子になった「[]」が不要になっていますね。ここで、先頭のletは定数を宣言することを表します(変数の場合にはvarを用います)。またObjective-Cの場合、オブジェクトはC言語的にはポインタ扱いになるため、変数名の先頭に「*」が必要でしたが、Swiftでは不要です。

なお、データ型の指定は宣言時に「変数名:型名」で指定しますが、宣言と同時に代入する場合には「型の推論機能」が働くため、型を省略可能です。

リスト8:型指定の省略

let now: Date = Date() // 型を指定
let now = Date()       // 型を省略

型名の「NS」が不要

Swift 3.0以降では、Foundationフレームワークに用意されているデータ型の型名から、NextStep由来の接頭辞「NS」がはずれました。例えば、Objective-CおよびSwift 2.xでは日付時刻を管理するデータ型は「NSDate」でしたが、Swift 3.0からは「Date」となっています。

Objective-C、Swift 2

リスト9:型名にNSがつく

NSDate

Swift 3.0以降

リスト10:型名にNSがつかない

Date

なお、連載が進むにつれておいおい説明しますが、Swiftではインスタンスを生成するためのひな形としてクラスの他に、構造体、列挙などがあります。Objective-Cの場合にはNSDateはクラスですが、SwiftのDateは構造体です。

フラグがSetに

たとえば、NSDateComponentsの年や月の値を指定するのに、NSYearCalendarUnitやNSMonthCalendarUnitといった、整数型(NSUInteger)のフラグが使用されていました。それらを「|」で連結することによって、コンポーネントを指定していました(3)。

Objective-C

リスト11:フラグ

NSUInteger flags = NSYearCalendarUnit | NSMonthCalendarUnit
| NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;

初期のSwiftでも同じくフラグが使用されていましたが、Swift 3.0以降では、Calendar.Component型の値を要素とするSetコレクション型に置き換わりました。Setは要素の重複を許さないコレクションで、「集合」と呼ばれます(3)。

Swift

リスト12:Setコレクション型

let comps: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute]

メソッドの書式と引数のラベルについて

Objective-Cでは「[ ]」を使用していたメソッドの書式が、Swiftでは以下のようにドット「.」を使用した一般的なものとなりました。

インスタンス.メソッド(引数)

次に、Dateオブジェクト「now」からDateComponetsオブジェクトを取り出すステートメントを示します(4)。

Objective-C

リスト13:DateComponetsオブジェクトを取り出す

NSDateComponents *dComps = [gCal components:flags fromDate:now];

Swift

リスト14:DateComponetsオブジェクトを取り出す

var dComp = gCal.dateComponents(comps, from: now)

注意点として、Swiftのメソッド(および関数)は、デフォルトで呼び出し時に引数にラベルを付ける必要のあることが挙げられます。ただし、ラベルは宣言時の指定によって省略可能です。上記のdateComponents()メソッドの場合、最初の引数「flags」のラベルは省略されていますが、2番目の引数「now」には「from」というラベルが指定されています。

文字列を画面に表示する関数

引数をコンソール画面に表示する関数は、Objective-CではNSLog()関数でしたが、Swiftではprint()関数になります(Swift 2.0まではprintln()関数)(5)。

Objective-C

リスト15:NSLog関数でコンソールに表示

NSLog(@"%ld年%ld月%ld日 %ld時%ld分", [dComps year], [dComps month],
    [dComps day], [dComps hour], [dComps minute]);

Swift

リスト16:print関数でコンソールに表示

print("\(dComp.year!)年\(dComp.month!)月\(dComp.day!)日 \(dComp.hour!)時\(dComp.minute!)分")

なおprint()関数の引数内の「\(変数や式)」は、文字列の「式展開」(String Interpolation)と呼ばれる機能で、「\(変数や式)」がその値に置き換わります。また上記の例では、「(dComp.year!」のように、変数の後に「!」が記述されています。Swiftでは新たに、値がある状態と値がない状態「nil」という2つの状態をとり得る「オプショナル型」が導入されました。これは値がない状態にしてメソッドを実行してしまうといったミスを避けるための配慮です。「!」はオプショナル型の値を強制的に取り出す指定です。

Swiftのプロジェクトの作成

Swiftのソースプログラムの雰囲気が理解できたところで、連載1回目の最後に、Xcodeを使用してSwiftのコマンドラインツール用プロジェクトを作成してみましょう。

1.「File」メニューから「New」→「Project」を選択します。

2. テンプレート選択ダイアログボックスで「macOS」→「Command Line Tool」を選択します。

テンプレートの選択

テンプレートの選択

3. 次の画面でプロジェクト名などを設定します。「Language」ではもちろん「Swift」を選択してください。

プロジェクト名などの設定

プロジェクト名などの設定

4. 次の画面で保存先を指定し「Create」ボタンをクリックするとプロジェクトが作成されます。

メインのソースとして「main.swift」が用意されています。ソースファイルには、あらかじめFoundationフレームワークのインポート、およびテスト用のprint()関数が用意されています。ステートメントをクリックすると「Quick Help」にヘルプが表示されます。

作成されたプロジェクト

作成されたプロジェクト

5. 「Build and Run」ボタンをクリックするとビルドが行われます。エラーがなければプログラムが実行され、コンソールに結果が表示されます。

実行結果の表示

実行結果の表示

次回は、Xcodeに組み込まれたSwiftプログラムのリアルタイムな実行環境であるPlaygroundと、アップルが提供するiPad版プログラミング学習アプリであるSwift Playgrounds(先日、日本語版がリリースされました)の紹介を予定しています。

東京都生まれ。早稲田大学理工学部卒業後、外資系コンピューターメーカーにSEとして8年間勤務。現在はフリーランスのテクニカルライター、プログラマー。

<主な著書>
きちんとわかる! JavaScript とことん入門(技術評論社)
基礎Python(インプレス)
他多数

連載バックナンバー

開発言語技術解説
第9回

Swiftでの文字列操作の基礎(その1)

2019/9/27
連載9回目となる今回は、Swfitによる文字列操作の基本を学びます。あわせてSwift 5で導入された新機能にもついても紹介します。
開発言語
第8回

Swiftでクロージャを理解する

2018/12/21
連載8回目となる今回は、モダンな言語に欠かせないクロージャについて解説します。
開発言語技術解説
第7回

Swiftのユーザ定義関数の活用

2018/9/13
連載7回目となる今回は、Swiftのユーザ定義関数のさまざまな活用方法を紹介していく。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています