SwiftのProtocol(プロトコル)とextension(エクステンション)
こんにちは!かずきです。
今回はProtocolとExtensionについて書いていこうと思います。
※今回の記事内のプロパティはコンピューテッド・プロパティ を指します。
Protocol(プロトコル)
Protocolとは
多言語ではよくインタフェースと呼ばれるものとほぼ同じものです。
継承したクラスで実装すべき、プロパティやメソッドを宣言する為に利用します。
継承先でのプロパティ・メソッドの存在保証
プロトコルで宣言したもは継承クラスで実装されていないとエラーが出るので、
継承クラスでのプロパティ・メソッドが利用可能な事が保証されます。
また、他のクラスと同じように継承先クラスは継承元であるプロトコルの型の変数で保持する事が出来ます。
実装例
protocol HogeHogeProtocol { var Fuga:String {get set} func FugaFuga() -> Void }
このようなプロトコルを用意して、
class HogeHogeHoge : HogeHogeProtocol { var fuga:String! // 実装しないとエラーになる var Fuga: String { get { return self.fuga } set { self.fuga = newValue } } // 実装しないとエラーになる func FugaFuga() -> Void{ print(self.Fuga) } }
このように継承・プロトコル実装します。
var hogehoge:HogeHogeProtocol = HogeHogeHoge() hogehoge.Fuga = "ほげほげ" hogehoge.FugaFuga() // ほげほげ
そうするとプロトコルで宣言されたメソッドを継承クラスで利用出来ます。
extension(エクステンション)
extensionとは
extension(拡張)の意味の通り、クラス・データ型・構造体などの機能を拡張する機能です。 プロパティとメソッドを実装する事ができます。
今回はデータ型とクラスの拡張を紹介します。
データ型の拡張
データを処理するプロパティ
このようなプロパティを書くと
extension Int { // パーセントから割合に変換 var Rate: Float { return (Float)(self) / 100.0 } } print(80.Rate) // 0.8
このようにデータ型に格納している値を処理追加する事が出来ます。
データ型そのものに機能を追加するプロパティ
プロパティの前にstatic
をつける事で、
extension Int { static var Zero : Int { return 0 } } print(Int.Zero) // 0
データ型そのものを拡張する事ができます。
データの処理をするメソッド
引数が必要な処理や保持しているデータ内容を変更したい場合などはプロパティでは実装出来ないので、メソッドを使って拡張します。
extension Int { func Square(x : Int) -> Int64 { var result:Int64 = Int64(self) for _ in 0...x { result = result * Int64(self) } return result } } print(12.Square(x: 10)) // 8916100448256
クラスの拡張
クラスの拡張もデータ型と同じように行えますが、
self
で取得できる物がデータでは無く、クラスのインスタンスになります。
※基本的にプロトコル実装以外での自作クラスの拡張は処理が分散して分かりにくくなるので、
クラス内に実装する事をおすすめします。(出番はライブラリなどの拡張のみ)
またエクステンションはクラス外の扱いになるので、private
の変数やメソッドにはアクセス出来ません。
// HogeHoge.swift class HogeHoge { private var hogeStr = "HogeHoge" }
// HogeHogeExtension.swift extension HogeHoge { public var HogeStr:String { get { return self.hogeStr // エラー } } }
プロトコル実装などをクラス外に書いて整理したい場合にも使えます。
この際、クラスとプロトコル実装のエクステンションを同一ファイル内に書く場合は
fileprivate
という同一ファイル内でのみアクセス可能なアクセス指定子を活用する事が出来ます。
extensionでプロトコル実装をする場合は以下のように記述します。
extension 元のクラス : 実装するプロトコル
fileprivate
を活用して実装するとこんな感じになります。
// HogeStrProperty.swift protocol HogeStrProperty { var HogeStr:String{ get } }
// HogeHoge.swift class HogeHoge { fileprivate let hogeStr = "HogeHoge" } extension HogeHoge:HogeStrProperty { public var HogeStr:String { get { return self.hogeStr } } }
これを他ファイルから呼び出すと
// FugaFuga.swift class FugaFuga { //---省略--- func PrintHogeHoge() -> Void { let hogeHoge = HogeHoge() print(hogeHoge.HogeStr) // HogeHoge } // ---省略--- }
HogeHogeクラスのhogeStrの内容が表示されます。
まとめ
プロトコルは動作(鳴く・動くetc)は同じだけど、
クラス毎にデータ(鳴き声・速度etc)が違ったり、
処理(動き方etc)など違う物を宣言しておき、
継承クラスで確実に利用したい場合に利用するエクステンションはデータ型にプロジェクト内でよく使う処理などの追加や、
ライブラリの拡張・プロトコル実装などに利用する
上述のように使うと便利かと思います。
他にこんな使い方あるよ
いう方。ぜひコメントなどで教えて下さい!!
Swiftのプロパティ監視willset/didset
こんにちは!かずきです。 今回はSwiftの特徴的機能の一つ、プロパティ監視のdidset/willsetについて書いていきたいと思います。 前回に続き、Swift入門者向けの記事です。
プロパティとは
private var hoge: Int = 0 public var Hoge: Int{ set{ self.hoge = newValue } get{ return self.hoge } }
読み書きそれぞれ別の範囲でのアクセス制限を掛けたい場合などのアクセス制御に使用するものです。
上のようにsetで囲んで値を設定するsetter
とget囲んで値を渡すgetter
の2種類があります。
class HogeHoge { private var hoge: Int = 0 public var Hoge: Int{ get{ return self.hoge } } }
このようなプロパティを持つクラスがあるとして、次のようにアクセスすると。。。
let hogehoge = HogeHoge() print("\(hogehoge.Hoge)") // "0" // エラー hogehoge.Hoge = 1
値の取得には成功しますが、代入は失敗してしまいます。
このように外部からは取得しか出来ず、書き換えさせない場合や複数の変数の計算結果などを返したい場合によく利用されます。
プロパティとプロパティ監視の違い
プロパティは変数のアクセス制御
などに使用するのに対し、プロパティ監視は変数の変更を監視
するものになります。
なぜ変数の監視
なのにプロパティ監視
なのかと言うと、Swiftでは変数もプロパティの1種
として扱われているからです。
先程説明したプロパティと変数としてのプロパティの違いは次に説明します。
2種類のプロパティ
Swiftにコンピューテッドプロパティ
とストアドプロパティ
の2種類のプロパティがあります。
コンピューテッドプロパティ
先ほど紹介したものはこちらです。
プロパティそのものには値を格納出来ません
。
よくプロパティ
と呼ばれるものはこちらになります。ストアドプロパティ
一般的に変数
と呼ばれるのもです。
getter
setter
は利用出来ませんが、 こちらのプロパティには値を格納出来ます
。
Swiftではこのように2種類のプロパティとして扱われています。
説明はしましたが、Swiftでは変数もプロパティの一種くらいの認識で大丈夫だと思います。
プロパティ監視(willset / didset)
プロパティ監視は先程説明した通り変数の変更を監視する
ものです。
willset
・・・変数の値の変更前
に通知されるdidset
・・・変数の値の変更後
に通知される
var hoge:Int = 0 { willSet(newValue){ hoge = 0 // 値は変わらない } didSet(oldValue){ if(hoge < oldValue) { print("元の値より小さい値は代入できません。\(oldValue) -> \(hoge)") hoge = oldValue // 値は変わる(この場合willset / didsetには通知されない) } } }
willset
で自身の変数に代入しても値は変化しません
が、didset
内で代入した場合は値が変化します
。
didset
内で自身の変数を変化させた場合willset
didset
には通知されません。
プロパティ監視の使い所
先程の説明で処理の場合、setter
でも同じ内容の事をする事が出来ます。
主なプロパティ監視の使いどころしては、値が変化された場合に特定のメソッドを呼ぶ
イベントで通知する
などです。
まとめ
ここまで説明してきましたが、基本的にwillset / didset
で出来る処理はgetter / setter
でも出来ます。
ですが。。。
読み書きそれぞれ
別の範囲でのアクセス制限
を掛けたい場合はgetter
setter
で処理を記述する。値が変更された場合に処理を行いたい場合
willset
didset
で処理を記述する。
このような使い分けで利用する事により、
変数(ストアドプロパティ)プロパティ(コンピューテッドプロパティ)両方にアクセス可能な場所で
setterに必要な処理
を書いているので変数に代入
してしまって正しい処理がされないなどのリスクを回避する事が出来ます。
他にも使い分け方があれば是非教えて下さい!
Swiftの!と?の違いと使い方
初めまして! かずきです。
この頃Unityばかり触っているので、Swiftを忘れかけている。。。 という事でSwift入門者向けの記事を何回に分けて書いていこうと思います!
オプショナル型とは
オプショナル型は変数宣言時にデータ型名の後ろに?
をつけることで利用出来るnil許容型
です。
var hoge : Int? var hogehoge : int!
!
については後ほど説明致します。
オプショナル型のアンラップ
そのままオプショナル型の変数を利用しようとするとOptional()
に囲まれた状態で中に入った値を利用できません。
var hoge : Int? = 114514 print(hoge) // => Optional(114514)
強制アンラップ
名前の通り強制的にオプショナル型をアンラップする方法です。
オプショナル型変数の値がnil
の場合エラー
が発生します。
var hogehoge : String? = nil print(hogehoge!) // エラー hogehoge = "屋上あんだけど焼いてかない?" print(hogehoge!) // => 屋上あんだけど焼いてかない?
nilを許容しない
場面でアンラップする際に使用します。
オプショナルバインディング
オプショナルバインディングは、if文などの条件式として利用します。
オプショナル型変数の値がnil以外
場合、条件式左辺で宣言した変数にアンラップされた値が代入されます。
if let unwrapfuga = fuga { // fugaが`nil以外`の場合(アンラップされた値がunwrapfugaに代入されている) } else { // fugaがnilの場合(unwrapfugaは利用不可) }
nilを許容する
場面で、変数の値がnilでない
場合のみ処理を行いたい場合などに使用します。
オプショナルチェイニング
オプショナル型変数に含まれるメソッドやプロパティを利用する際に使用します。
オプショナルチェイニングを使用してメソッドやプロパティにアクセスした場合、戻り値は全てオプショナル型
となります。
※例えばこのようなクラスがあったとします。
class HogeHoge{ private var name : String = "yajuu" public var Name : String { get { return self.name } } public func IceTea() -> String { return "おまたせ!アイスティーしかなかったけどいいかな?" } }
変数hogehgeに値が入っている場合はオプショナル型の戻り値が帰ってきます。
var hogehoge : HogeHoge? = HogeHoge() print(hogehoge?.Name) // => Optional("yajuu") print(hogehoge?.IceTea()) // => Optional("おまたせ!アイスティーしかなかったけどいいかな?")
変数hogehogeがnil
の場合は戻り値もnil
になります。
var hogehoge : HogeHoge? = nil print(hogehoge?.Name) // => nil print(hogehoge?.IceTea()) // => nil
戻り値を強制アンラップ
かオプショナルバインディング
して使用します。
暗黙的アンラップ型
暗黙的アンラップ型はオプショナル型の一種で、使用時に自動で強制アンラップを行います。
// オプショナル型 var a : Int? = 10 var b : Int = 20 var c = a! + b // cc = 30 var a : Int? = 10 var b : Int = 20 var c = a + b // エラー // 暗黙的アンラップ型 var aa : Int! = 10 var bb : Int = 20 var cc = aa + bb // 自動的に強制アンラップされるので、ccは30になる
主に初期化時以外でのnilが許容されない場面で使用します。