Advanced Operators
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html を読む。
- Swiftの算術演算子はオーバーフローしない
Int16.max + 1
はエラー- オーバーフローする演算子
- オーバーフロー 加算
&+
- オーバーフロー 減算
&-
- オーバーフロー 乗算
&*
- オーバーフロー 加算
- 二項
static func + (left: T, right: T) -> T {…}
- 単項前置
static prefix func - (v: T) -> T {…}
- 複合代入
static func += (left: inout T, right: T) {…}
- デフォルトの代入演算子はオーバーロードできない
- 3項演算子はオーバーロードできない
- 等価
static func == (left: T, right: T) -> Bool {…}
プロトコルEquatable
をconform ==
を実装したら、ふつう!=
は実装する必要はないEquatable
プロトコルをconformすると宣言したら Swift が==
を合成してくれる場合- Equatable型の格納プロパティだけ持ってる構造体
- Equatable型の関連型だけ持つ列挙型
- 関連型を持たない列挙型
- カスタム演算子
operator
宣言は ファイルスコープでなければならない prefix operator ☹
static prefix func ☹ (v: T) -> T {…}
infix operator ☺: AdditionPrecedence
static func ☺ (left: T, right: T) -> T {…}
- 演算子の優先順位は Swift Standard Libraryのほうで決まっているみたい
さてLANGUAGE GUIDEがひととおり終わったので なんかiOSアプリを作ってみたい。ゲームとか。 LANGUAGE REFERENCEを読むのは重箱の隅ほど美味しそうだけど深みにハマっていくのもアレなので。
Access Control
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html を読む。
- アクセスレベルは5段階
enum AccessLevel { case `open` case `public` case `internal` case `fileprivate` case `private` }
- レベルの高低は、
open
が最高(最も制限が少ない)、private
は最低(最も制限が多い) open
とpublic
の違いはmoduleを越えてsubclass化とoverrideできるかどうか- エンティティは より低い(より制限的な)他のエンティティが出てくるようなやつは 定義できない
public
な変数は、internal
やfileprivate
,privte
な型で定義できない- 関数は、パラメータや返り値の型より高いアクセスレベルにできない
- デフォルトのアクセスレベルは
internal
- 多くの場合、コードに明示的なアクセスレベルを指定する必要はありません。
- 単純な単一ターゲットアプリを書く場合は、
internal
で充分要件を満たしている - フレームワーク開発では APIを
open
かpublic
にする - 単体テスト可能なのは
open
かpublic
だけどinternal
なのをテストしたいなら@testable
アトリビュートを付ける - 型のアクセスレベルは、メンバーの暗黙のアクセスレベルになる
- privateかfileprivateにすると、メンバーの暗黙のアクセスレベルはprivateかfileprivateになる
- internalかpublicにすると、メンバーの暗黙のアクセスレベルはinternalになる
- タプルのアクセスレベルは、中身から最も低いアクセスレベルのやつになる
- 関数の暗黙のアクセスレベルは、パラメータと返り値型のなかから最も低いやつになる
- 列挙型の個々の
case
は、列挙型のレベルと同じになる。個々のcase
に異なるレベルはつけられない - 列挙型のrawValueと 関連付けられた値は、列挙型と同じか高いアクセスレベルでなければならない
- ネストされた型の暗黙のレベル
private
型にネストされた型は、private
fileprivate
型にネストされた型は、private
public
かinternal
型にネストされた型は、internal
- サブクラスは、スーパークラスより高いレベルにできない
- オーバーライドは、スーパークラスでのアクセスレベルより高くできる
- 定数変数プロパティは、その値の型のレベルより高くできない
- subscriptはインデックス型・返り値型より高くできない
- プロパティ等のgetterとsetterの暗黙のアクセスレベルは、そのプロパティ等と同じになる
- setterにgetterより低いアクセスレベルを与える
public private(set) var x = 0
- イニシャライザは 型の レベルより低くできる
required
イニシャライザは型のレベルと同じでなければならない- デフォルトのイニシャライザの暗黙のレベルは、型のレベルと同じ(
public
以外)public
型のデフォルトイニシャライザの暗黙のレベルはinternal
- 構造体のmemberwiseイニシャライザは、
- メンバーのレベルのうち一番低いやつが
private
ならprivate
- メンバーのレベルのうち一番低いやつが
fileprivate
ならfileprivate
- それ以外は
internal
- メンバーのレベルのうち一番低いやつが
- プロトコルの各要素のアクセスレベルは、プロトコルのレベルと同じで、違えられない
- プロトコル継承は、元のプロトコルのレベルまで持てる。高くはできない。
- プロトコルのconformは、型のレベルよりも低く出来る
- 型がプロトコルをconformするという文脈は、型のレベルとプロトコルのレベルの最小値
- extension で定義したメンバーの暗黙のレベルは、元の型のものと同じ(最大internal)
- extensionにデフォルトのレベルを指定
private extension {…}
- extensionでプロトコルをconformする場合は extensionのレベルは指定できない。替わりにプロトコルのレベルがデフォルトのレベルになる
- 元の型と同じソースファイルに書いたextensionから、元の型のprivateにアクセスできる
ふつうinternalだよね〜と言われたので、とりあえず全て無指定で生きて行こうと思うの。
Memory Safety
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/MemorySafety.html を読む。
- シングルスレッドでも競合は起きる
- 次のすべての条件を満たす2つのアクセスがある場合、競合が発生する
- 少なくとも1つは書き込み
- メモリ内の同じ場所にアクセスする
- 期間が重複する
- メモリアクセス期間には「瞬時」か「長期」がある
- ふつうは瞬間的。いくつかの場合「長期アクセス」がある。長期アクセスは他のアクセスとオーバーラップできる
inout
パラメータの書き込みアクセスは、他の非inout
パラメータの評価が全て終わった後、 関数呼び出しの期間続く- 競合するとランタイムエラー
- ひとつの解決方法は、値をコピーすること
- 2つの
inout
パラメータをとる関数に、同一の変数を渡すとコンパイルエラー - 構造体の
mutating
メソッド は メソッド呼び出しの期間self
への 書き込みアクセスを持つ x.mutatingMethod(&x)
→コンパイルエラー- 構造体・列挙型・タプルの値の一部を変更すると値全体が変更される場合に競合するおそれがある
- 次の条件が適用される場合、コンパイラは、構造体プロパティへの重複アクセスが安全であることを証明できる
ちょっと意図しないところで出そうで怖いけど、 コンパイルエラーでもランタイムエラーでも「modification requires exclusive access」と言われるので、 言われたらまあ気づけるかな、と思う。
Automatic Reference Counting
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html を読む。
- ARCは「ちょうどうまくいく」
- 参照カウンタが適用されるのはクラスのインスタンスだけ
- 強い参照の循環ができるとメモリが開放されない
- 強い参照の替わりに、弱い参照か、所有されていない参照を使えば良い
- 弱い参照はオプショナル型の 変数
var
でなくてはならない weak var tenant: Person?
- 所有しない参照は参照されるインスタンスの寿命が同じか長いときに使われる
- 非オプショナル型として定義される
- deallocateされたあと、参照先にアクセスするとランタイムエラー
unowned let customer: Customer
- お互いに参照しあうとどっちかがOptionalになる←非Optionalにするとコンストラクタでselfにアクセスする2段階初期化問題があるから
- 暗黙にunwrapされたOptionalな型にする方法がある
var capitalCity: City!
- クロージャはクラスのインスタンスと同様に参照型
- クロージャ本体が
self
にアクセスするとself
をキャプチャーする - そのクロージャを
self
が指すオブジェクトのプロパティ値に入れると、循環参照ができてしまう - 「キャプチャリスト」をつくることで、この問題を解決する
- クロージャの開始
{
と(引数リスト)との間に - 引数リストと返り値方が型推論される時は、クロージャの開始
{
とin
との間に [unowned self, weak delegate = self.delegate!]
weak
でキャプチャしたやつは Optional型
やっぱり参照カウンタからは逃れられなかったよ
Generics
Genericsは鬼門だわ
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/Generics.html を読む。
- genericな関数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {…}
- 使うときは
swapTwoValues(&someValue, &anotherValue)
適切に型推論される - 実際にはSwift標準ライブラリの
swap(_:_:)
を使うが良い - 総称型
struct Stack<Element> {…}
- 総称型へのextension
extension Stack {…}
- 型の制約
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {…}
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {…}
- プロトコルは総称型にできないので、替わりに
associatedtype
を使う - プロトコルをconformする実装では
typealias 抽象型 = 具体型
- ↑型推論がうまくいけば typealias はなくてもいける
- 既存の型へのextensionでプロトコルをconformするとき、関連する型も含まれる
- プロトコルで関連する型の制約
associatedtype Item: Equatable
- プロトコルで関連する型の制約
associatedtype T: U where T.Item == Self.Item
- generic関数で制約
func f<C1: C, C2: C> (_ a: C1, _ b: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {}
- 総称型へのextensionで型パラメータの制約
extension Stack where Element: Equatable {…}
- プロトコルへのextensionで関連型の制約
extension Container where Item == Double {…}
- プロコトルへのextension中のsubscriptにwhere句
subscript<Indices: Sequence>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int {…}
- プロコトルへのextension中のsubscriptにwhere句
protocolを既存の型にextentionで適合させるときに 思いもよらない組み合わせを生み出しそうで恐ろしい。
たぶん自分ではそんなに凝ったことは書かないと思うので問題はないけど 他人が書いたコードを読める自信は無い。
Protocols
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/Protocols.html を読む。
protocol
は 他の宣言の内側にネストできないextension
と同じファイルスコープ、なのにエラーメッセージが違うのはなぜだ- プロパティはインスタンスと型を要求できる。格納型か計算型かどうかは指定できない。名前と型だけ
- 読み取り専用か、読み書き可能かは指定できる
- プロトコルが読み書き可能を要件したら、定数格納プロパティや読み取り専用計算プロパティでは要件を満たせない
- 読み取り専用なら、どんな形式のプロパティでも満たせる
- プロトコルでは
var
キーワード 読み書き可能は{ get set }
読み取り専用は{ get }
- 型プロパティ・型メソッドはプロトコルでは
static
でも、クラスではclass
宣言できる - こんなところで "John Appleseed"
- インスタンスメソッドと型メソッドを要求できる
- 可変引数できる。パラメータのデフォルト値は、プロトコルの定義ではできない
- プロトコルで
mutating
メソッドは構造体と列挙型でのみ使われる クラスの実装ではmutating
不要 - 指定イニシャライザ または 便利イニシャライザ のどちらも
- プロトコルでイニシャライザを要求したら、実装では
required
修飾子をつけてrequired init(〜) {…}
final
クラスではrequired
修飾子は要らない。なぜならfinal
クラスはサブクラス化できないから- プロトコルでfailableなイニシャライザを要求したら、実装はfailableでもnonfailableでもどっちでも要件を満たす
- プロトコルでnonfailableなイニシャライザを要求したら、実装はnonfailableなイニシャライザか、暗黙にunwrapするfailableなイニシャライザが要件を満たす
- プロトコルで要件したイニシャライザを実装したクラスから派生したサブクラスでoverrideしたら
required override init(〜)
- delegateなパターンは弱参照で
weak var delegate: XxxxDelegate?
- プロトコルのコンポジション
ProtocolA & ProtocolB
- プロトコルをconformしてるかどうかは
is
キャストはas?
かas!
で - プロトコル定義でオプショナルな要求は
optional
修飾子をつけて - オプショナルな要求は Objective-C との相互運用でavailableなので
@objc
属性をつけて - オプショナルな要求は 構造体と列挙型には適合できない
- オプショナルな要求は Optionalな型になるので、使うときは
?
をつけてopttional-chaining - 型のextensionで、型にprotocolをconform
- プロトコルのextensionで、メソッドをデフォルト実装
- プロトコルのextensionで、制約
extension Collection where Element: Equatable {…}
Extensions
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/Extensions.html を読む。
- オリジナルソースコードにアクセスできなくても機能性を追加できる(遡及モデリング)
extension
を宣言できるのはファイルスコープだけ- extensionでプロトコル適合は Protocol の章で
- 総称型の extension は Generics の章で
let aMarathon = 42.km + 195.m
- extensionで便利イニシャライザを追加できるけど、指定イニシャライザや deinitializer は追加できない
- 別のモジュールで宣言された構造体に、エクステンションでイニシャライザを追加すると、新しいイニシャライザは定義モジュールのイニシャライザを呼び出すまで、
self
にアクセスできません。 3.repetitions { print("Hello!") }
mutating func square() { self = self * self }
- extensionにネストされた型
extension Int { enum Kind {〜} …}
- ネストされた型へのextension
extension OuterType.InnerType {〜}