Swift 文法周りについて
関数の書き方
func 関数名 (引数1:引数1の型, ...) -> 戻り値の型 {
return 戻り値
}
func getArea ( r: Double ) -> Double {
let pi = 3.14
return pi * r * r
}
print( getArea( r:2 ) )
関数ラベル(Function Argument label)とは
関数を呼び出す時に指定する引数の名前です。例えば以下の関数があるとします。
func someFunction1(firstParam: String, secondParam: String) {
print("First=\(firstParam), Second=\(secondParam)")
}
呼び出す場合には、引数にfirstParamとsecondParamを指定します。
someFunction1(firstParam: "AAA", secondParam: "BBB")
これが関数ラベル
関数定義のパラメータ名と関数呼び出し時のラベル(つまり関数ラベル)が同じですが、別々にすることもできます。
// 関数ラベルに別名をつける
func someFunction2(first firstParam: String, second secondParam: String) {
print("First=\(firstParam), Second=\(secondParam)")
}
someFunction2(first: "AAA", second: "BBB")
呼び出す場合に毎回関数ラベルを指定するのがうざったいという場合には、省略することもできます。別名指定の時に_(アンダースコア)を指定します。
// 関数ラベルと省略する.
func someFunction3(_ firstParam: String, _ secondParam: String) {
print("First=\(firstParam), Second=\(secondParam)")
}
someFunction3("AAA", "BBB")
変数・定数宣言について
var で変数宣言
let で定数宣言
基本的には型を推測して変数を作れる。
Floatだけはそうもいかないみたい。
後述で型宣言の方法を記述
//宣言方法
var 変数名 = 値
let 定数名 = 値
var str = "Hello Objective-C"
str = "Hello Swift"
let str = "Hello, playground"
型 : type宣言
// 文字列
var member: String = "Yamada Taro"
// 整数
var age: Int = 26
// Double: 64bitサイズの浮動小数点型
var height: Double = 182.4
// Float: 32bitサイズの浮動小数点型
var weight: Float = 88.3
// 真偽値
var isMan: Bool = true
変数を先に宣言する方法
var member: String
member = "Yamada"
var age: Int
age = 26
var height: Double
height = 182.4
var weight: Float
weight = 88.3
var isMan:Bool
isMan = true
非オプショナル型の縁数は値を入れないとつかえない
var piyo: String
println(piyo)
# => エラーして実行できない
//Variable 'piyo' used before being initialized
オプショナル型の変数は何も値を入れなくてもnilとして使うことができるが、非オプショナル型の変数の場合はnil以外の値を代入するまで使うことができない。
代入の文法について
「 = 」 前後の空白があるかないかを揃えないといけない。
// OK
var str01 = "Hello Swift"
var str02="Hello Swift"
// Errors
var str03= "Hello Swift"
var str04 ="Hello Swift"
型 : typeについて
符号付
Int8, Int16, Int32, Int64
符号なし
UInt8, UInt16, UInt32, UInt64
整数型
型 | 最大値 | 最小値 |
---|---|---|
Int8 | 128 | -128 |
Int16 | 32767 | -32768 |
Int32 | 2147483647 | -2147483648 |
Int64 | 9223372036854775807 | -9223372036854775808 |
Uint8 | 255 | 0 |
Uint16 | 65535 | 0 |
UInt32 | 4294967295 | 0 |
UInt64 | 18446744073709551615 | 0 |
let maxOfint64 = Int64.max
print(maxOfint64) //9223372036854775807
let minOfint64 = Int64.min
print(minOfint64) //-9223372036854775808
let maxOfuint64 = UInt64.max
print(maxOfuint64) //18446744073709551615
ところで Int と UInt はどれだけの範囲になるのでしょうか
32bit環境であれば Int32、Uint32 と同じ
64bit環境であれば Int64, UInt64 となります。
Int8は8ビット幅の数値しか対応しないので、
こういう計算をすると、overflowのエラーになります。
let a: Int8 = 100
let b: Int8 = 100
let c = a * b // overflow
これらビットタイプのIntは、bit演算、シフト演算、ローテート演算などbitでの計算や暗号化などに使われます。
ANDのビット演算の例:
// bit演算
let a: UInt8 = 252 // 1111 1100
let b: UInt8 = 63 // 0011 1111
let c: UInt8 = a & b // AND
print(c) // 60 = 0011 1100
浮動小数店
型 | 詳細 |
---|---|
Float | 32bit浮動小数点 |
Double | 64bit浮動小数点 |
Float32 | 32bit浮動小数点:Floatのエイリアス(別名) |
Float64 | 64bit浮動小数点:Doubleのエイリアス(別名) |
Float80 | 80bit浮動小数点 |
Float96 | 96bit浮動小数点 |
CGFloat | 32bit/64bit浮動小数点:環境による |
Float80とかFloat96とかありますが、これは何でしょうか?
基本的にはHardwareがこのビット数に対応していないといけないようで、主にmacOS用のようです。CPUがARMでは対応できないということなのでiOSでは使えないようです。(iOSとmacOSが統合と>いう噂もあります)
String
Date
Bool
オプショナル型(Optional Value)について
通常の変数とは異なり、空の値 nullの状態を保持することができる変数
Swiftではnilと表記
オプショナル型宣言
型の後ろに?をつける
宣言と同時にnilが代入される
text = nilのように初期化の必要性はない。
var text: String?
var text2: String = nil
// nil cannot be assigned to type
// 非オプショナルにはnilを代入できない
オプショナルの配列・ディクショナリ
//オプショナル型の配列を宣言
var fruits1: Array<String>?
fruits1 = ["apple", "orange", "melon"]
print(fruits1)
//オプショナル型のディクショナリを宣言
var fruits2: Dictionary<String, Int>?
fruits2 = ["apple": 100, "orange": 80, "melon": 500]
print(fruits2)
Optional(["apple", "orange", "melon"])
Optional(["melon": 500, "apple": 100, "orange": 80])
オプショナルのアンラップ
var num1: Int = 1
var num2: Int? = 2
print(num1 + 1); // 2
print(num2 + 1); // エラー
//error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or ‘?’?
**intとint?**は異なるため、オプショナル型にintを代入しようとするとエラーがでる。
アンラップ(unwrap)とはラップ(包装)の反対語でオプショナル型で包まれた変数を解除するような意味合いで使われます。
Optionalは値を包み込むラップ(包み紙)のイメージです。オプショナル型は値をOptionalというラップ1枚で包み込んでいます。すると、たとえ中身がない(=nil)状態でも包み紙だけは存在するため、とりあえず扱うことができます。
オプショナル型の値がnilでも使えるのは、この包み紙が存在しているためです。しかし、この便利な包み紙があるために値はラッピングされ、直接扱うことができません。扱うためにはラップを取り除き、中身の値を取り出さなくてはいけません。
この包み紙を取り除き、値を取り出すことをアンラップと言います。
https://qiita.com/maiki055/items/b24378a3707bd35a31a8
オプショナル型はnilが入っている可能性があるため、それを明示的にするためアンラップする必要がある。
アンラップはオプショナル型の変数の後ろに**!**をつける
//オプショナル型を宣言
var num: Int?
num = 100
print("num:\(num!)")
ただオプショナル変数がnilの場合はアンラップするとエラーになる。
オプショナルのバインディング(Binding)
条件式にオプショナル型を使えて、nilの時はfalse, それ以外はtrue
オプショナル型を用いて比較することをBindingと呼ぶ
//オプショナル型の宣言
var num: Int?
//Binding
if let sum = num {
print("sum:\(sum)")
} else {
print("値が設定されていません。")
}
値が設定されていません。
暗黙的なオプショナル宣言 (Implicitly Unwrapped Optional)
オプショナル宣言時に?をつけるのではなく、
!をつけると暗黙的なオプショナル宣言をできる
var text: String!
こうすると、使用時にアンラップする必要がなくなる。
ただ、nilが入ったまましようした時はエラーになっちゃう。
初期値はnilでも使うときには値が格納されていることがわかっているときには暗黙的なオプショナル型を宣言したほうが安全です。
//暗黙的なオプショナル型の宣言
var num: Int!
print(num) // null
num = 100
print(num) // 100
num = nil
print(num + 10) // エラー
オプショナル型 | 非オプショナル型 | |
---|---|---|
データ型(Stringの場合) | String?またはString! | String |
nilの代入 | 可能 | 不可 |
初期値 | nil | 何もない |
変数に値を代入しない場合 | nil | エラーが起きる |
オプショナル型の出力
アンラップせずに出力すると Optionalという記述で異なる。
var hoge: String?
hoge = "Hello World"
println(hoge)
// => Optional("Hello World")
var hoge: Int? = 10
println(hoge)
// => Optional(10)
アンラップの種類
- Forced Unwrapping (強制的アンラップ)
- Optional Binding (オプショナルバインディング)
- Optional Chaining (オプショナルチェイニング)
Forced Unwrapping (強制的アンラップ)
オプショナル型を強制的にアンラップする方法。
どんな値が入っていても関係なくアンラップして値を取り出す。
オプショナル型の変数の後ろに**!**をつける
var optional: Int? = 10 // オプショナル型
// そのまま出力
println(optional)
// => Optional(10)
// 強制的アンラップ("!"をつけて)で出力
println(optional!)
// => 10
var a: Int = 10 // 非オプショナル型
var b: Int? = 10 // オプショナル型
a + b!
# => 20
欠点として、もしも中身がnilの場合はエラーがでてアプリケーションが落ちる
var hoge: Int?
hoge!
# => オプショナル型の変数hogeはnilなのでアンラップするとエラーする
Optional Binding (オプショナルバインディング)
強制的アンラップだと、中身関係なくアンラップしてしまうため、nilが入ってしまったままエラーが出る可能性がある。
if分などと組み合わせることで、解決する方法が Optional Binding
var hobby: String? // オプショナル型
if let unwrappedHobby = hobby {
println(unwrappedHobby)
}
else {
println("趣味はありません")
}
オプショナルバインディングの特徴として条件式でnilかどうかを確かめること
if var 変数名 = オプショナル型の変数 { 処理 }
この形がオプショナルバインディング
この変数はlet でも varでもどっちでもおk
つまり、この例で言えば、unwrappedHobbyにhobbyを代入して
中身がnilの場合は、if分の条件式がfalseになるし、
中身がnilではない時は、if分の条件式がtrueになる。
そしてこの新しい変数のunwrappedHobbyを処理の中で使えば、
アンラップされている値として代入されているunwrappedhobbyを使うことができる。
var hobby: String? = "プログラミング"
# unwrappedHobbyに"プログラミング"が代入される
if let unwrappedHobby = hobby {
# hobbyがnilではないため、ここの処理が呼ばれる
println(unwrappedHobby)
# => "プログラミング"
}
var hobby: String?
if let unwrappedHobby = hobby {
println(unwrappedHobby)
}
else {
# hobbyがnilのため、ここの処理が呼ばれる
println("趣味はありません")
# => "趣味はありません"
}
Optional Chaining (オプショナルチェイニング)
オプショナルちぇイニングもオプショナル型の中身がnilのときに安全に実行する処理方法
オプショナルチェイニングの使い方はオプショナル型の変数のあとに"?"をつけます。
オプショナル型の変数?
オプショナルチェイニングはオプショナル型の変数に続けてプロパティを取得したり、メソッドを呼び出す場合に使用します。
オプショナル型の変数?.プロパティ
オプショナル型の変数?.メソッド()
オプショナルチェイニングもオプショナル型の変数がnilかどうかでその後の処理が変わります。
オプショナル型の変数に値が入っている場合、オプショナルチェイニングは変数をアンラップし、中身を取り出します。そして、それに続くプロパティを取得したり、メソッドを呼び出すことができます。
しかし、ここで一つ気をつけなくてはいけません。
それは、オプショナルチェイニングに続けて取得したプロパティやメソッドの戻り値はオプショナル型になるということ
var human: Human? = Human()
// nilでないためプロパティが取得できる
human?.name
// => "Optional(シンボ)"
// nilでないためメソッドを呼び出せる
human?.hello()
// => "Optional(こんにちは)"
オプショナル型の変数の中身がnilの場合、オプショナルチェイニングはnilを返します。そしてnilを返した後、それに続く処理をすべてキャンセルします。
つまりnilに対してプロパティを取得しようとしたり、メソッドを呼び出すことがなくなるため安全に処理を記述することができます。
var human: Human?
// humanがnilのため、nilが返る
human?.name
// => nil
// humanがnilのため、nilが返る
human?.hello()
// => nil
先ほど、「オプショナルチェイニングを使って取得した値はすべてオプショナル型となる」と言いましたが、その理由はオプショナル型の変数がnilのときは返る値がnilになるためです。
nilを扱うことができるのがオプショナル型だけなので、nilが返る可能性がある以上、値はオプショナル型にしなくてはいけません。
Swiftのオプショナル型の使いこなし
タプル
データ型がごちゃまぜで入れられる配列的存在
let item = ("りんご", 100, 0.08)
print(item.0, item.1, item.2 * 100) // りんご, 100, 8
タプルの各要素を別々の変数に受け取ることもできる。
let item(name, price, tax) = ("りんご", 100, 0.08)
print(name, price, tax * 100) // りんご, 100, 8
タプルにラベルをつけて参照することもできる。
let item = (name: "りんご", price: 100, tax: 0.8)
print(name, price, tax * 100) // りんご, 100, 50
Class
メンバ関数(メソッド)(インスタンスメソッド)とクラス関数(メソッド)
classの定義内に書ける関数は二種類ある
メンバメソッドとクラスメソッド
メンバ関数(メソッド)(インスタンスメソッド)
ラス内の定義で、特に修飾子無しで記述したメソッドはメンバ関数になります。
メンバ関数は状態(自分のインスタンスのプロパティの値)に依存します。
また、メンバ関数では自分のインスタンスはself. として参照できます。
class Hoge {
func getName() -> String {
//処理内容
}
}
クラス関数(メソッド)
メソッドを宣言する時にfuncの前にclassを記述したメソッドはクラスメソッドになります。
クラスメソッドは状態(自分のインスタンスのプロパティの値)に依存しません。
クラスメソッドの特徴として、インスタンス化しなくてもコールすることができます。
class Hoge {
class func getName() -> String{
//処理内容
}
}
メソッドのコール
メンバ関数はインスタンスの後ろに続けて書くことによって呼び出しますが、クラスメソッドはクラス名の後ろに続けて書くことによって呼び出します。
// メンバ関数
let name = Hoge()
name.getName()
// クラスメソッド
let name = Human.getName()
Classの定義と継承
クラスの継承とは親クラスの属性を引き継いで新たなクラスを作成することです。継承するクラスをサブクラス又は子クラス、継承されるクラスをスーパークラス又は親クラスと呼びます。
Swiftでは、ユーザ定義のクラスをベースクラスとすることができ、特定のクラスを継承する必要はありません。
継承するとサブクラスにはスーパークラスで宣言されたすべてのオブジェクトとメソッドが使えるようになります。
Swiftではクラスの継承を サブクラス名: スーパークラス名 と定義します。
class Monster {
var name: String
var level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
func attackMonster(enemy: Monster) {
print("\(self.name)は\(enemy.name)を攻撃した。");
}
}
/* Slimeクラス(Monsterクラスを継承) */
class Slime: Monster {
func escapeFromMonster(enemy: Monster) {
print("\(self.name)は\(enemy.name)から逃げた。");
}
}
let monster = Monster(name: "モンスター", level:3)
let slime = Slime(name: "スライム", level:2)
monster.atackMonster(slime) // モンスターはスライムを攻撃した。
slime.atackMonster(monster) // スライムはモンスターを攻撃した。
slime.escapeFromMonster(monster) // スライムはモンスターから逃げた。
上の例はMonsterクラスを継承したSlimeクラスを定義したものです。Slimeクラスはインスタンス化した時にイニシャライザが呼ばれますが、Slimeクラスではinitを宣言していないのでスーパークラスであるMonsterクラスのinitが呼ばれます。
スーパークラスのattackMonsterメソッドにはMonsterクラス、Slimeクラスのどちらのオブジェクトからもアクセスできます。
オーバーライド
class Monster {
var name: String
var level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
func attackMonster(enemy: Monster) {
print("\(self.name)は\(enemy.name)を攻撃した。");
}
}
/* Slimeクラス(Monsterクラスを継承) */
class Slime: Monster {
override func attackMonster(enemy: Monster) {
print("\(self.name)は\(enemy.name)をおちょくった。");
}
}
let monster = Monster(name: "モンスター", level:3)
let slime = Slime(name: "スライム", level:2)
monster.atackMonster(slime) // モンスターはスライムを攻撃した。
slime.atackMonster(monster) // スライムはモンスターをおちょくった。
slime.super.atackMonster(monster) // スライムはモンスターを攻撃した。
スーパークラスで既に定義されているatackMonsterメソッドをサブクラスで再宣言するにはoverrideをサブクラスの再宣言するメソッドの先頭に記述する必要がある。
SlimeクラスのオブジェクトからスーパークラスのatackMonsterメソッドを呼びたい場合、superを記述することでスーパクラスのメソッドを呼ぶことができます。
extension
- クラス
- 構造体
- 列挙型
に対して機能を拡張できます。書式は以下のとおりになります。
継承の概念との違い
同時にクラスを拡張するという意味では、継承の概念とも似ています。
しかし、継承の場合は、あるクラスの機能を拡張するのに別なクラスを用意する必要があります。
それに対し、extensionはすでにあるクラスを動的に拡張することができるのです。
また、継承の場合は、既存のメソッドを上書きするオーバーライドの概念がありますが、extensionにはそれがありません。
つまり、extensionではもともとある機能の変更はできないのです。
extensionの記述
// パーソン
struct Person {
let name: String // 名前
var age: Int // 年齢
}
この構造体を以下のようにしてインスタンスを生成します。
let taro = Person(name: "山田太郎", age: 35)
このままでは何のメソッドもありません。そこで
extension Person{
func show(){
print(self.name,":",self.age)
}
}
とすると、ここにshow()というメソッドが追加されます。これにより
taro.show()
とすると、
山田太郎:35
のような結果が出力されます。
extensionでは、以下のような機能の拡張が可能です。
- 計算プロパティ(クラス、構造体に対して)
- タイププロパティ
- メソッド
- subscript
- イニシャライザ(初期化子)
- ネストされた型
- プロトコルに対する準拠
データ型の拡張と範囲検出
extension Int {
static var zero: Int { return 0 }
mutating func inverse() {
self = -self
}
}
// Intのzeroプロパティを表示する
print(Int.zero)
// nに3を代入し、符号を逆転させる
var n = 3;
n.inverse();
print(n);
0
-3
値型に対して自分自身や付随するプロパティを変更する場合、mutatingをメソッドに付けます。
Weak Unowned Strong
Swift メモリについて
Swiftのメモリはガベージコレクションではなく**ARC(Automatic Reference Counting)**によって管理されています。
ARCとは
新しいインスタンスを初期化する際に、ARCはそのインスタンスの型や保有するプロパティに応じたメモリを確保します。
そして、そのインスタンスが必要なくなったらARCは確保しているメモリを解放します。
しかし、まだ必要なインスタンスにもかかわらずメモリを解放してしまった場合、その後インスタンスにアクセスしようとすれば当然クラッシュしてしまいます。
これを防ぐために、ARCはそれぞれのインスタンスがいくつのプロパティや変数,定数から参照されているかをカウントし、その参照カウントがゼロにならない限りメモリは解放しないようになっています。
では実際に例を用いてARCの挙動を見てみましょう。
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
上記のPersonクラスは、init 及び deinit が呼び出されたときにメッセージを出力します。
次のコードでは、Personクラスの新しいインスタンスに対する複数の参照を示します。
var reference1: Person? = Person(name: "John Appleseed")
// Prints "John Appleseed is being initialized
var reference2: Person? = reference1
var reference3: Person? = reference1
ohn Appleseedという名前を持つ新しいPersonインスタンスが初期化され、
そのインスタンスは現在reference1,2,3の3つの変数からの参照を持つことがわかります。
ここでreference2,3の参照を切ってみましょう。
reference2 = nil
reference3 = nil
インスタンスへの参照は、reference1からの参照が1つ残っているためまだメモリは解放されません。
(deinit が呼び出されていませんね)
では最後にreference1の参照も切ってみましょう。
reference1 = nil
// Prints "John Appleseed is being deinitialized"
インスタンスへの参照カウントがゼロになり、無事 deinit が呼び出されました。
Strong / 強参照
これまで扱ってきた参照はすべて 強参照 と呼びます。
この強参照を無意識に使いまわしていると、例えば2つのインスタンスがお互いに強参照していまい永遠にメモリが解放されない、ということが起きてしまいます。
イメージしやすいように以下で例を示します。
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
PersonクラスとApartmentクラスはそれぞれお互いをプロパティとして持ちます。
optionalとして定義しているため、初期値はnilです。
次にそれぞれのインスタンスを強参照する変数を宣言します。
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
Personインスタンスの持つapartmentプロパティにApartmentインスタンスを、
Apartmentインスタンスの持つtenantプロパティにPersonインスタンスを代入します。
john!.apartment = unit4A
unit4A!.tenant = john
それぞれのインスタンスがお互いに強参照していることがわかります。
なので、変数john,unit4Aの強参照を切っても参照カウントはゼロになりません。
john = nil
unit4A = nil
deinit が呼び出されないことが確認できますね。
解放されないメモリが蓄積されていくことになります。
変数からの強参照を切った後の参照関係は以下のようになります。
このインスタンス間の強参照は残り続け、メモリが解放されることはありません。
この問題を解決するためにあるのが 弱参照 と 非所有参照 です。
弱参照 (weak)
弱参照はARCの参照カウントに加算されません。
よって、強参照と弱参照を1つずつ持つインスタンスは参照カウントが1であり、1つの強参照を切ればインスタンスは解放されます。
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
変更点は1箇所のみ、Apartmentクラスのtenantプロパティを弱参照で宣言しています。
そして先の例と同様の参照関係を作ります。
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
Personインスタンスは変数johnからの強参照とApartmentインスタンスからの弱参照を持つため、参照カウントは1となります。
変数johnにnilを代入して強参照を切ってみましょう。
john = nil
// Prints "John Appleseed is being deinitialized
Personインスタンスの参照カウントはゼロになり、 deinit が呼び出されていることがわかります。
非所有参照 (unowned)
非所有参照 (unowned)
弱参照と同様に非所有参照はARCの参照カウントに加算されません。
弱参照との使い分けは
- 弱参照
-- 参照対象が自身よりも先にメモリが解放されるときに使う - 非所有参照
-- 参照対象が自身と同じかより後にメモリが解放されるときに使う
という具合に行います。
上記の例では、Apartmentの居住者tenantは必ずしもいるとは限らず、またコロコロ変わるものであるため弱参照を使用するのが適切ですね。
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
CustomerクラスとCreditCardクラスはそれぞれお互いをプロパティとして持ちます。
Customerはクレジットカードを持っていないかもしれませんが、
CreditCardは必ずその持ち主Customerがいることになります。
ゆえにCustomerクラスはcardプロパティをCreditCard?型として持ち、
CreditCardクラスはcustomerプロパティを非所有参照として宣言しています。
次にCustomerインスタンスを強参照する変数を宣言し、
そのCustomerインスタンスのプロパティcardにCreditCardインスタンスを代入します。
var john: Customer? = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
ここで、現在の参照関係は以下のようになっています。
Customerインスタンスは、変数johnからの強参照とCreditCardインスタンスからの非所有参照を持つため参照カウントは1となり、
CreditCardインスタンスは、Customerインスタンスからの強参照を1つ持つため参照カウントは1となります。
それでは、変数johnにnilを代入してCustomerインスタンスへの強参照を切ってみましょう。
john = nil
// Prints John Appleseed is being deinitialized
// Prints Card #1234567890123456 is being deinitialized
まずCustomerインスタンスの参照カウントがゼロとなりメモリが解放されます。
次にCreditCardインスタンスはCustomerインスタンスからの強参照を失い、参照カウントがゼロとなってメモリが解放されます。
非所有参照 (unowned) と暗黙的アンラップ型 (!)
typealias
Swiftで型の別名を付ける事のできる
Intのaliasとして使います。
typealias MyInt = Int
let int: MyInt = 1
print(int)
print(int.value)
元のクラスにメソッドを追加した場合の動き
typealias MyInt = Int
extension Int {
func myMethod() {
print("My Method")
}
}
let int: MyInt = 1
print(int.myMethod())
Aliasの型
dynamicTypeで型を調べた所、元の型になりました。
typealiasの名前通り、Intの別名を作っているだけで実態は同じもののようです。
typealias MyInt = Int
let int: MyInt = 1
print(int.dynamicType) // → Int
関数のAlias
関数の別名を付ける事もできます。
メソッドの引数部分がすっきりするので、複雑な関数については別名を付けると良さそうです。
typealias MyBlock = () -> ()
func method(block: MyBlock) {
block()
}
タプルのAlias
タプルに対して別名を付ける事もできます。
簡単なアプリを作る際は、クラスの代わりにタプルを使うのも手軽で良さそうです。
typealias User = (id: Int, name: String)
let users = [
User(id: 1, name: "Name1"),
User(id: 2, name: "Name2"),
User(id: 3, name: "Name3"),
]
print(users.first!.id)
クラス内のtypealias
別クラス内で定義した型を使う場合はClassForAlias.MyIntという形で利用する必要があります。
class ClassForAlias {
typealias MyInt = Int
}
class MyClass {
func method() {
ClassForAlias.MyInt.max
}
}
protocolのtypealias
エラー処理
@State
@Stateはプロパティの宣言時に使えるSwiftUIのカスタム属性です。
プロパティの値とUIの状態を自動的に同期する仕組みを実現します。
@Stateをつけたプロパティには次の2つの機能が付加されます。
値が更新可能になる。
SwiftUIのViewはStructの為、通常ではプロパティを更新できませんが、@Stateを付けると更新可能になります。
値の変更がSwiftUIよってモニタリングされる。
モニタリングしたプロパティに変更があった場合、対象プロパティを参照しているViewが自動的に再描画されます。これにより、プロパティの値とUIの状態の同期が実現されます。
struct ContentView: View {
@State private var isPowerOn = false // 電源の状態を保持するプロパティ
var body: some View {
VStack {
/// 電源ボタン
Button(action: {
self.isPowerOn.toggle() // クリックでisPowerOnの値を反転
}) {
Image(systemName: "power") // 電源ボタンの画像
}
/// 電源状態表示
Text(isPowerOn ? "電源ON" : "電源OFF")
}
.font(.largeTitle)
}
}
@Stateで宣言されたプロパティはそれを保持するViewと、そのプロパティを参照する配下のViewからしかアクセスできません。この為、private修飾子の使用をAppleでは推奨しています。
外からは値を設定できない為、プロパティは初期値が必要になります。
View内のみで使うデータフローを扱うのに適している仕組みです。
SwiftUIのドル記号は、状態変数(@Stateが付いている変数)の参照を渡す役割があります。
SwiftUIでは、PickerやStepperなど状態変数自体を変更したいビューがあります。
単なる代入だと、状態変数自体を変えることは出来ませんよね。
なので、参照を渡して、変数自体を変えれるようにします。
ここで登場するのが、ドル記号($)です。
ドル記号をつける事によって、@Stateが付いた変数の参照を参照を渡すことが出来るようになるのです。
ドル記号($)は、状態変数(@Stateを付与した変数)の参照を渡す役割
参照したい状態変数名の前に$を付けることで使用できる
アクセス修飾子
internal
internalは使うことは多いけど実はあまり目にしないもの。
なぜならデフォルト(記述を省略した場合)の修飾子がinternalだからだよ。
private
クラスなどの宣言内のみからアクセスできるよ。
fileprivate
fileprivate はその名の通り、同一ファイル内からのみアクセスできるアクセス修飾子だよ。
public
別のターゲット内からでもアクセスできるようになるよ。
overrideは可能だけど、継承ができないよ。
open
publicのアクセス範囲に加え、継承もできるよ。
publicやopenは基本的にはFrameworkなんかに設定するアクセス権だよ。
もし、クラス内でも変更されたくないときはletを使うほうが良いし、なるべく厳しいアクセス修飾子(private > fileprivate > internal > public > open)で作っていくのが基本だよ。
Staticについて
class Cat {
static let sex: String = "male"
func eat() {}
static func meow() {}
}
// メソッド呼び出し時にCatクラスのインスタンスが毎回生成される。Cat().eat()
// Catクラスのインスタンスを生成せずに参照する。Cat.meow()
static キーワードを使ってプロパティやメソッドを宣言すれば、そのクラスの全インスタンスで共通して利用することができる。
参照する際にはインスタンス生成されないので、メモリの使用量を抑えることができる。
Selfとself
大文字のSelfと小文字のselfがSwiftには存在する
【Swift】大文字のSelfまとめ
#【swiftのself.〜の時のselfの意味とは?】