Swiftの変数の宣言とデータ型について

はじめに
今回は、Swiftにおける変数の宣言方法と、標準で用意されているデータ型の概要を駆け足で見ていくことにしましょう。前回、前々回で紹介したPlaygroundを使用して実際にコードを入力して確認しながら読み進めても良いでしょう。
変数の宣言について
Swiftでは変数の宣言を、varキーワードを使用して行います。
var 変数名:型
例えばInt型の変数ageを宣言して、値に「44」を代入するには次のようにします。
リスト1:変数の宣言と代入
1 | var age:Int |
2 | age = 44 |
次のように1行で記述しても構いません。
リスト2:宣言と代入は同時に行える
1 | var age:Int = 44 |
型の推論機能
上記の例では、Objective-Cにおける変数宣言と同じように、型を明示していました。これを「型アノテーション」と呼びます。Swiftでは、宣言と同時に値を代入する場合、型アノテーションなしに型を判定してくれる「型推論機能」が用意されています。次に例を示します。
リスト3:型推論の例
1 | var age = 44 // Int型 |
2 | var name = "山田太郎" // String型 |
3 | var rate = 104.5 // Double型 |
上記のように、代入する値が整数の場合にはInt型、文字列はString型、浮動小数点数の場合にはDouble型と判断されます。なお、カンマ「,」で区切ることにより、複数の変数を1行で宣言できます。
リスト4:カンマ「,」用いて複数の変数をまとめて宣言する
1 | var year = 2018, pref = "東京都", weight = 55.5 // <-Int型、String型、Double型 |
Swift 3以降では、値の型名を取得するためにtype(of:)関数を使用します(Swift 2以前に型名を取得するのに使用されていたdynamicTypeプロパティは廃止されました)。
リスト5:type(of:)関数の使用例
1 | var age = 44 |
2 | print(type(of: age)) // -> Int |
定数として宣言する
varキーワードの代わりにletキーワードで宣言した場合は定数扱いとなり、後から値を変更できなくなります。
リスト6:定数の宣言にはletを用いる
1 | let birthYear = 1985 // letで定数として宣言 |
2 | birthYear = 1986 // <-エラー |
整数型について
整数の値を管理するデータ型の代表がInt型です。変数を宣言して値を代入すると、型推論によりInt型となります。それ以外にもデータ長に応じて、「符号付整数型」と「符号なし整数型」が各4種類、合計8種類の整数型が用意されています。
整数型
符号付き整数型 | 符号無し整数型 | データ長 |
---|---|---|
Int | UInt | 32ビット環境では32ビット、64ビット環境では64ビット |
Int8 | UInt8 | 8ビット |
Int16 | UInt16 | 16ビット |
Int32 | UInt32 | 32ビット |
Int64 | UInt64 | 64ビット |
IntおよびUIntは、32ビット環境と64ビット環境でビット長が異なります。ただし、XcodeのSwiftで作成可能なMacアプリは、64ビット版のみです。また、iOS11以降は32ビットアプリは完全に対象外となったため、現在ではInt = Int64、UInt = UInt64と考えて良いでしょう。
なお、Int型以外の整数型を使用するには、型アノテーションが必要です。例えばInt8型の変数pointを宣言して、値に「95」を代入するには次のようにします。
リスト7:Int型以外の整数型は型アノテーションが必要
1 | var point:Int8 = 95 |
また、これらの型は全て構造体(struct)として定義されています。例えば、Int8型はInt8構造体として定義され、maxプロパティで最大値、minプロパティで最小値を表示できます。
リスト8:Int8構造体
1 | print(Int8.max) // -> 127 |
2 | print(Int8.min) // -> -128 |
浮動小数点型について
浮動小数点型は、小数点以下の値を持つ数値(実数)を表すための型です。SwiftではDouble型とFloat型の2種類の構造体が用意されています。
浮動小数点型
型 | データ長 |
---|---|
Float | 32ビット |
Double | 64ビット |
型アノテーションなしで宣言するとDouble型になります。
リスト9:型アノテーションなしで実数を代入するとDouble型に
1 | var num1 = 3.14 // Double型 |
Float型の変数を宣言するには、明示的に型を指定する必要があります。
リスト10:Float型の宣言
1 | var height:Float = 174.5 // Float型 |
Swift 3以降では、型のデータ長を調べるには「MemoryLayout<T>.size」を使用します(Swift 2以前に用意されていたsizeof()関数は廃止されました)。
リスト11:型のデータ長を調べる
1 | print(MemoryLayout<Int>.size) // -> 8 |
2 | print(MemoryLayout<Float>.size) // -> 4 |
3 | print(MemoryLayout<Double>.size) // -> 4 |
Swiftは型に厳格
Swiftは型に厳格な言語です。例えばObjective-Cの場合、次のようにint型の値をdouble型の変数に代入できました。
リスト12:Objective-Cの例
1 | int a = 5; |
2 | double b; |
3 | b = a; |
上記の例のように、代入によって値の精度が変わらないケースでも、Swiftではエラーになります。
リスト13:Swiftではエラーとなる
1 | var a:Int = 5 |
2 | var b:Double; |
3 | b = a // <- エラー |
Int型の値をDouble型の変数に入れたい場合は、次のようにDouble型のイニシャライザを使用して、Int型をDouble型に変換してから代入する必要があります。
リスト14:明示的に変換してから代入
1 | b = Double(a) |
前述の例では整数と浮動小数点数の間での変換でしたが、Int8型とInt型といったように、同じ整数型同士でも、型が異なる場合には変換が必要になります。
リスト15:整数型間でも変換が必要
1 | var num1:Int8 = 34 |
2 | var num2:Int = Int(num1) + 1 // Int8型をInt型に変換 |
文字列はString型
Swift標準の文字列は、String構造体のインスタンスです。Objective-Cと同様に、文字列リテラルはダブルクォートで囲みます。次にString型の変数strを宣言して、"こんにちは"を代入する例を示します。
リスト16:String型の宣言と代入
1 | var str:String = "こんにちは" |
型アノテーションを省略した場合には、自動的にString型と見なされます。
リスト17:文字列リテラルの代入でString型と見なされる
1 | var str = "こんにちは" |
また、Objective-Cでは文字列の連結にはstringWithFormat:メソッドを使用していましたが、Swiftでは+演算子で簡単に連結できます。
リスト18:文字列の連結は+演算子
1 | var yourName = "山田" + "花子" // "山田"と"花子"を連結 |
NSStringのクラスの利用
Swiftでも、Objective-Cで使用されていたFoundationフレームワークのNSStringクラスを使用することが可能です。
リスト19:NSstringクラスを利用できる
1 | var name:NSString = "田中一郎" |
NSString型とString型は相互変換可能です。NSString型の文字列を、String型にキャストするには次のように「変数名 as String」を使用します。
リスト20:NSstring型をString型にキャスト
1 | var str:String = name as String |
文字数を正しくカウントできる
Swiftの文字列に特徴的なのは、いくつかのビュー(view)という概念で文字列を操作できるようになっている点です。例えば、CharacterViewでは文字列を画面での見た目の文字として、UTF16Viewでは文字エンコーディングUTF-16のバイト列データとして扱うことが可能です。ここで言う「見た目の文字」とは、エディタでのカーソルの移動単位と考えてください。
String型の詳細についてはまた別の機会に解説するので、ここではCharacterViewを使用すると、文字列を見た目の文字そのものとして扱えると覚えておいてください。
例えば、Swift 3までは文字列の長さを求めるには、charactersプロパティでCharacterViewを取得し、そのcountプロパティを使用していました。
リスト21:文字列の長さを求める(Swift 3まで)
1 | var str:String = "こんにちは" |
2 | print(str.characters.count) // -> 5 |
Swift 4では、String型のcountプロパティで文字列の長さを取得することができるようになりました。
リスト22:文字列の長さを求める(Swift 4)
1 | print(str.count) // -> 5 |
これは、Objective-Cで標準であったNSString型のlenghtプロパティを使用した結果と同じです。
リスト23:NSString型のlengthプロパティ
1 | var nsStr:NSString = "こんにちは" |
2 | print(nsStr.length) // -> 5 |
NSStringとStringの大きな相違が、「1文字」の取り扱いです。例えば、前者はUTF-16のバイト列で文字を管理し、lengthプロパティはその長さを戻すため、必ずしも見た目の文字数とは限りません。それに対してcountプロパティはきちんと文字数を戻します。
例を示しましょう、Unicodeでは「だ」という文字は、「だ」(U+3060)の他に「た」(U+305f)+ 濁点(U+3099)の組み合わせでも表現できます。NSString型では後者の表現を2文字と認識しますが、String型の場合は正しく1文字となります。次に、「たかだ」という文字列の長さを求める例を示します。
リスト24:「たかだ」の長さは?
1 | var str = "たかた" + "\u{3099}" // たかだ |
2 | print((str as NSString).length) // -> NSString型では4文字 |
3 | print(str.count) // -> String型では3文字 |
またUnicode 6.0で追加された国旗の絵文字は、2文字の国コードリストから構成されますが、UTF-16では2文字ともサロゲートペアとなるため、NSString型では4文字としてカウントされます。それに対してString型では、正しく1文字とカウントされます(Unicodeは当初全ての文字を2バイトで表現することを目的としていましたが、近年新たにUnicodeに組み込みたい文字が増えてきたため領域が不足してきました。そのため、新たに追加する文字を4バイトで管理する仕組みとして、サロゲートペアが考案されました)。
リスト25:国旗の絵文字は何文字になるか?
1 | var str = "🇯🇵" // U+1F1EF + U+1F1F5(strは日本の国旗の絵文字だが、環境によってはJPと表示される) |
2 | print((str as NSString).length) // -> NSString型では4文字 |
3 | print(str.count) // String型では1文字 |
コレクションについて
Swiftでは、コレクションとして配列(Array)、辞書(Dictionary)、集合(Set)といった構造体が用意されています。これらは、それぞれObjective-Cで使用されていたFoundationフレームワークのNSArray(およびNSMutableArray)、NSDictionary(およびNSMutableDictionary)、NSSet(NSMutableSet)といったクラスを置き換えるものです。またSwiftでは、フレームワークではなく、言語に組み込まれた構造体として定義されています。コレクション全体については連載が進むにつれて詳しく説明することにして、ここでは配列(Array構造体)を簡単に紹介しましょう
配列を使ってみる
Foundationフレームワークの配列の場合、ミュータブルな配列はNSMutableArrayクラス、イミュータブル(変更できない)な配列はNSArrayクラスと個別に用意されていました。それに対してSwiftでは、varで宣言すればミュータブル、letで宣言すればイミュータブルになります。配列リテラルの書式はObjective-Cと同じく、要素をカンマで区切り、全体を「[ ]」で囲みます。
リスト26:配列リテラルの書式
1 | // ミュータブルな配列を宣言 |
2 | var countries = ["アメリカ", "日本", "中国", "イギリス"] |
3 | // 2番目の要素を変更する |
4 | countries[1] = "ドイツ" |
for~inループで配列の要素をイテレートできます。
リスト27:配列の要素をイテレート
1 | for c in countries { |
2 | print(c) |
3 | } |
4 |
5 | 実行結果 |
6 | アメリカ |
7 | ドイツ |
8 | 中国 |
9 | イギリス |
タプルについて
Objective-Cにはない、一連のデータを管理する機能として、Swiftには「タプル(tuple)」があります。タプルとは複数の値をまとめて、一つの値のように扱う仕組みです。Pythonなどのメジャーな言語にも用意されているので、ご存知の方も多いでしょう。タプルのリテラルは次のような書式になります
(値1, 値2, 値3, ...)
値は型が異なっていても構いません。次に、顧客の番号、名前、年齢を管理するタプルcustomerを、型アノテーション付きで宣言する例を示します。
リスト28:タプルの宣言例
1 | var customer:(Int, String, Int) = (101, "江藤三四郎", 43) |
型アノテーションを省略しても構いません。
リスト29:型アノテーションを省略した宣言例
1 | var customer = (101, "江藤三四郎", 43) |
タプルの各要素には、「変数名.番号」でアクセスできます(番号は先頭の要素を0とする整数値)。
リスト30:タプルの要素へのアクセス
1 | print(customer.1) // -> 江藤三四郎 |
2 | customer.2 = 44 // 3番目の要素を44に変更 |
それぞれの要素に名前を付ける
値の前に「名前:」を記述することにより、それぞれの要素に名前を付けることができます。
リスト31:タプルの要素に名前を付ける
1 | var customer = (number:101, name:"江藤三四郎", age:43) |
以上で、「変数名.要素名」でアクセスでます。
リスト32:要素名を用いてアクセス
1 | var hisName = customer.name // nameをhisNameに代入 |
2 | customer.age = 44 // ageを44に変更 |
タプルの値をそれぞれ変数に代入する
タプルの要素の値を個別の変数に代入するには、左辺の「( )」内にカンマで区切って変数を記述します。
リスト33:タプルの要素を個別の変数に代入
1 | var customer = (number:101, name:"江藤三四郎", age:43) |
2 | var (number, name, age) = customer |
このとき値が不要な要素は、その部分にアンダースコア「_」を記述します。
リスト34:不要な要素は「_」を記述
1 | var colors = ("red", "blue", "green") |
2 | var(c1, _, c3) = colors // c1に"red", c3に"green"が代入される |