iOS開発の知識
iOS開発の知識
iOSに関する知識が欠如している。
勉強してまとめます。
参考
UIView
とUIViewController
について
UIView
とは
矩形領域を表示する機能を持ったクラス。
UIView
および、そのサブクラスを用いて画面を構築する。
UIViewController
View
のライフサイクルの管理と画面遷移の機能を持つクラス
ライフサイクルについて
ライフサイクル
-
loadView
カスタムViewの初期化を行う。Storyboard
やxib
などのInterfaceBuilder
を利用しているときはこのメソッドは呼び出さない。 -
viewDidLoad
viewがロードされた後に呼び出される。 -
viewWillAppear
viewが表示される直前 -
viewWillLayoutSubviews
layoutSubViews
の前 -
viewDidLayoutSubviews
layoutSubViews
の後 -
viewDidAppear
完全に遷移が完了し、画面上に表示された時に呼ばれる。 -
viewWillDisappear
viewが表示されなくなる直前で呼ばれる。 -
viewDidDisappear
完全に遷移が行われ、画面上に表示されなくなった時に呼ばれる。
weak
とunowned
、strong
について
Swiftのメモリはガベージコレクションでなく、ARC(Automatic Reference Counting)
によって管理されている。
ARC
とは
それぞれのインスタンスがいくつのプロパティや変数、定数から参照されているかをカウントする。
ゼロになったらメモリを解放し、ゼロにならない限りメモリは解放しない。
strong(強参照)
何も修飾語をつけないと強参照になる。
二つのインスタンスがお互いに強参照し合うと、循環参照
を起こす。
weak(弱参照)
ARCの参照カウントに加算されない。
参照対象が自身よりも先にメモリが解放される時に使う。
unowned(非所有参照)
ARCの参照カウントに加算されない。
参照対象が自身と同じか、後にメモリが解放される時に使う。
class
とstruct
とenum
の使い分け
class
参照型
参照の共有
インスタンスを共有したい時に使う。
struct
値型
コピーオンライト
という機能
代入を行なった時点でコピーは行われず、値が異なった時にコピーが行われる。
- 不必要なコピー作業を省き、パフォーマンスの向上
継承と委譲について
継承
基底クラスを拡張する時に使う。
委譲
クラス内でインスタンスを生成し、その機能を利用する。
Array
とSet
、Dictionary
について
Array
配列
Set
集合
- ユニークなデータを保持する
- 順番は保持されない
オペレーション
intersection
、symmetriceDifference
、union
、subtracting
Dictionary
辞書
GDP
について
GDP(Grand Central Dispatch)
非同期処理を容易にするためのC言語
ベースの技術。
- タスクをキューに追加して非同期処理
- スレッドを直接管理しない
- 予め準備された空きスレッドを使う(スレッドプール)
dispatch queue
GDP
で用いるキュー。
実行方式
直列、並列の二種類
作り方
main queue
メインスレッドで実行する直列dispatchQueue
ユーザインターフェースの更新は常にmain queue
で行う。
let mainQueue = DispatchQueue.main
global queue
並列dispatchQueue
実行優先度を指定して取得
実行優先度
QoS(Quality of Service)
という
- userInteractive
- userInitiated
- default
- utility
- background
attributes
.concurrent
は並列DispatchQueue
を生成する時につける。
GDP
では難しいケース
複雑な非同期処理には向かない。
- 条件に応じたタスクのキャンセル、タスク同士の依存関係の定義など
-
Operation
、OperationQueue
クラスを利用する
Singletonについて
- クラス同士で密結合となる
- 単体テストがしづらくなる
- 依存関係がわかりづらい
- 再利用性が低下する
- 状態を初期化できるようにする(依存性を注入する)
mutating func
自身の値を変更するときにmutating
キーワードを書く
extension Int {
mutating func plusOne() {
self += 1
}
}
var i: Int = 0
i.plusOne()
print(i)
@escaping
引数に受け取るクロージャに対してattribute
として付与する。
クロージャがスコープから抜けても存在し続ける時に必要になる。
- クロージャがスコープ外で強参照される時
- クロージャを非同期的に実行するとき
class A {
private let storedClosure: () -> ()
init(closure: @escaping () -> ()) {
storedClosure = closure
}
}
必要なこと
-
self.
が必要 -
[weak self]
を使用することで、循環参照を防ぐ
defer
- スコープを抜ける時に実行したい処理を定義する。
- 複数の
defer
を記述した場合は記述した順番の逆順に呼ばれる。 -
break
やreturn
を使用してスコープを抜けてしまうと呼ばれない。
@objc
とdynamic
について
@objc
- 隠し引数の数が原因で
objective-c
からswift
が呼べない -
@objc
をつけたメソッド、プロパティはobjective-c
から正しく利用するために必要。
dynamic
-
objective-c
のランタイムを用いるために用いる
iOSアプリの構造
アクセス修飾子の詳細
- private
- fileprivate
- internal
- public
- open
private
同スコープ内からのみ呼び出せる。
fileprivate
同ファイル内からのみ呼び出せる。
internal
同モジュール内からのみ呼び出せる
public
別モジュールから呼び出せるが、継承やオーバーライドが不可能
open
別モジュールから呼び出せる。継承やオーバーライドが可能
readonly
にするには
swiftでprivate(set)
を利用する。
private (set) var name: String = "apple"
AutoLayoutについて
様々な画面サイズの端末でも柔軟に表示することができる仕組み。
AutoLayout
は必要なのか
なぜ- iOS端末は種類が多く、それぞれの画面のサイズが異なる
- 絶対位置でUI部品を配置してしまうと、画面のサイズの変更に対応できない
AutoLayout
を使用する
コードで
NSLayoutConstraint
かNSLayoutAnchor
を用いる。
NSLayoutAnchor
の方が、コード量が少なく、制約も明確に宣言できる。
ライブラリ
SnapKitというラッパーライブラリが便利。
Bridging headerについて
XCode
の機能。
Swift
からObjective-C
を呼び出すときに使う。
#import "Foo.h"
Objective-C
からSwift
を呼び出すとき
#import "{Product Module Name}-Swift.h"
Swiftのモジュールとは
Swiftにおけるモジュールの定義
- コードのまとまりの単位。フレームワークやアプリケーションとして作られた一単位のコードのまとまりで、swiftでは
import
キーワードを用いることでインポートされる。
名前空間
Swiftでは暗黙的な名前空間が用いられている。
名前空間とはソースコード上で冗長な命名規則を用いなくても名前の衝突が起こらないようにし、それを容易に記述できるようにする概念
swiftでJSONの扱い
SwiftyJson
を用いると簡潔に行える。
Codable
, Encodable
, Decodable
について
typealias Codable = Decodable & Encodable
Codable
Codable
はJSONデータオブジェクトとSwiftのclass, struct
の相互変換に用いられる。
Decodable
自身をJSONなどの外部表現からオブジェクトにデコードできる型。(bytes から objectへの変換)
Encodable
自身のオブジェクトをJSONなどの外部表現にエンコードできる型。(object から bytesへの変換)
CodingKeys
エンコード・デコードのキーが一致しないときに用いる。
case名をプロパティ名、rawValueをエンコード結果のフィールド名として定義する。
struct Coordinate: Codable {
var latitude: Double
var longitude: Double
var elevation: Double = 0 // default value
enum CodingKeys: String, CodingKey {
case latitude = "another_key"
case longitude
}
}
Result型について
非同期処理など、エラーが発生する場合に使う。
ある処理を行うときに、それの成否によって処理を変えるために用いる。
Result型
public enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
throws
との違い
-
throws
はエラー型を指定できない。 -
Result
はManual Propagation
、throws
はAutomatic Propagation
Manual Propgation
はguard if switch
など
Automatic Propagation
はdo try catch
Result
型が使われるのか
なぜAutomatic Propagation
でうまくいかないときがある。
- エラーを保持して、引数や戻り値として取り回したいとき(非同期処理など)
エラーハンドリング
Preference Keyとは
親Viewから子Viewへのデータのアクセスを可能にするプロトコル。
inoutについて
関数に値参照渡しするときに使う。
読みだすときには&
をつける
func plus(a: inout Int) {
a += 1
}
var a = 0
plus(&a)
予約語を変数名として使う
基本的には使ってはいけないが、どうしても使わなければいけないとき(APIの
JSONのkeyで使われているときなど)に使う
- バッククォートで囲う
var `open`: String
Equatableについて
オブジェクト同士の比較を可能であることを保証するプロトコル。
実装は以下のようにする。
struct Fruit: Equatable {
var name: String
var emoji: String
// 自身と同じ型を2つ受け取る静的メソッド
static func == (lhs: Fruit, rhs: Fruit) -> Bool{
return lhs.emoji == rhs.emoji
}
}
Comparable
大小比較ができることを保証するのはComparable
Hashableについて
ハッシュ値を提供できることを表すプロトコル。
これに適合する方はDictionary
やSet
の要素として扱うことができる。
struct S: Hashable {
let v1, v2 : String
// `Hashable`に適合させるには`var hashValue: Int { get }`が必要です。
var hashValue: Int {
return v1.hashValue ^ v2.hashValue
}
// `Hashable`は`Equtable`を継承していますので、`==`演算子も必要です。
// (`hashValue`の一致後に、さらに`==`で同等の値であるか評価します)
static func ==(lhs: S, rhs: S) -> Bool {
return lhs.v1 == rhs.v1 &&
lhs.v2 == rhs.v2
}
}
ジェネリクスについて
ジェネリクスを使うメリット
- 柔軟で再利用可能な型や関数を定義することができる
- コードの重複を防ぐことにつながる
associatedtype
について
protocol
でジェネリクスを用いる場合に使う。
AnyとAnyObject
AnyObject
全てのクラス型のインスタンスを表すことができる。
struct, enum, closure, tuple
は値型なので扱うことはできない。
-
Objective-C
のメソッドとプロパティを使用する場合に使用する。 -
class only protocol
の宣言にも使われる
Any
全ての型のインスタンスを表すことができる。
as, as?, as!, isについて
アップキャスト
より汎用的な型への変換
キャストできることが保証されるため、as
を用いる。
ダウンキャスト
より具体的な型への変換
キャストできることが保証されていないため、as?, as!
を用いる。
is
型チェックに用いる
var i = 0
print(i is Int)
delegateとは
デザインパターンの一つ。
あるクラスは、他のクラスのインスタンスに処理を任せることができるもの。
- ポリフォーフィズム(多態性)の一例
- プロトコルで定義されたデリゲートメソッドを実行する
- 委譲先を意識する必要がない
- 再利用ができる
protocol
はAnyObject
を継承する
delegateを使うためのprotocol
を使用するときはclass-only protocol
(参照型)でなければいけない。
-
weak
を使った弱参照を行うため -
weak
は参照型にしか使えない
protocol ProtocolDelegate: AnyObject {}
swiftにおけるメモリリーク
メモリリークとは
あるオブジェクトをメモリに確保した後、そのオブジェクトが解放されずにメモリ内に残ってしまう状態。
convenience
とdesignated
、required
イニシャライザ
convenience
イニシャライザ
同一クラスの別のイニシャライザを呼び出すことができる。
designated
イニシャライザ
基本となるイニシャライザ。init() {}
required
イニシャライザ
サブクラスでの実装が必須となる。
UIStackViewについて
-
AutoLayout
のラッパークラス - 複数の
View
を水平、垂直方向にレイアウト可能 - デバイスの回転、スクリーンサイズなどに動的に対応可能
StateObjectについて
StateObject
を持つView
がObservableObject
を所有することになる。つまり、最初にオブジェクトを作ったときにはStateObject
を使用する。他のView
から参照するときは、ObservedObject
を使う。
The rule is this: whichever view is the first to create your object must use @StateObject, to tell SwiftUI it is the owner of the data and is responsible for keeping it alive. All other views must use @ObservedObject, to tell SwiftUI they want to watch the object for changes but don’t own it directly.
UIViewのレイアウトのライフサイクル