同僚や、知人が書かれていることもあり、Swift実践入門を読んでSwift言語への理解を深めてみました。
私個人のスキルでいうと、公式のThe Swift Programming Languageの2.1向けのものや、Functional Swiftとかの英語書籍を読んだり写経したりして簡単なSwiftプログラムを書いたり読んだりはできる感じです。iOSエンジニアとして会社でコードを書き続けているわけではないです。
内容は、うろ覚えだったり把握していなかったとところを補足することができ、言語自体の学びを整理することにとても役立つ書籍でした。iOS周辺の開発になると、こことは別にAPIへの理解も必要なのですが、私はそこはまだまだ足りていない…
全体的に、Swiftの様々な機能をどういう形で使うと良いのか、というところで参考になりました。以下、私が読んでいてメモしておきたかったところをメモしておきます。
2章
Swiftにおける範囲型、Optionalの話とか、復習になった。特に、 ? や ! のforce unwrapの話。実行時に評価されて無理やり値を取り出す ! と、 .none になる ? の話とか。
テストコードだと、例えばEarlGreyのようなやつだと実行時評価が必要なことが多く、 ! を使ったりしていた。けれど、Swiftの理想でいうとやっぱり ! は思想に反する(実行時にしか評価されないし、クラッシュするし)のでやぱりよくないのだなーと。
as? によるダウンキャストや as! as? によるアップキャストの話もちらほら。
3章
switch の制御で以下のようにかける形、Erlang/Elixirを学んでいたので自然な感じだった。こういうの書き始めると、Androidの世界にも時折欲しいと思う時がある…
switch a / 2 {
case let b where b < 2:
print("lower than 2")
case let b where b >= 2:
print("more than or equal to 2")
default:
print("no")
}
繰り返しの for ... in ... {} は使ったこともあるのですが、 case 使う場合もinを支えたのですね。把握していなかった…
for value in 1...4 { print("\(value)") }
for case let (2, value) in [(1, "a"), (2, "b"), (3, "c")] { print("\(value)") }
switchの中、 break を置くことはよくやることですね。Javaとかやっていると。これ、breakしないと多くの場合は次々と case を実行していくのですが、swiftでは明示的に fallthrough を与えないといけないのですね。
let a = 10
switch a / 2 {
case let b:
print("first") // "first\n"
fallthrough
case 5:
print("after fallthrough") // "after fallthrough\n"
break
print("after break")
default:
print("no")
}
あと、 breakやcontinueの遷移先を制御するために label をつけることができるとは知らなかった…
4章
@discardableResultがあったのですね。 _ = someMethods で処理を書いていた。どのみち、使わない、使わなくて良いことを明示するスタイル。
Swift、以下のような形で副作用をもたせた書き方もできたのですね。JavaとかRubyといったオブジェクト指向の書き方では書いていたのですが、Swift(のstructとか)Elixirとかみたいな関数型をメインで触っていると、こういうことが頭から離れることが多い…
func add(_ x: Int, _ y: Int, _ a: inout Int) {
a += x + y
}
var a: Int = 10
add(10, 10, &a)
a // 30
7章
protocolの話。
Swiftはprotocol oriented programmingと言われるように、このprotocolが重要になります。
associatedTypeによる、protocolでは抽象的な文字を定義しつつ、具体的な型はそれを受け継いだstructなどで定義する。それらの定義は明示しなくとも類推されて補完されもします。個人的には、Swiftは明記によって安全性を表現しているので、推論ばかりに頼りたくない(明示していきたい)なーとも思います。
protocol SomeProtocol {
associatedtype AssociatedType
var value: AssociatedType { get }
func someMethod(value: AssociatedType) -> AssociatedType
}
struct SomeStruct : SomeProtocol {
typealias AssociatedType = Int
var value: AssociatedType
func someMethod(value: AssociatedType) -> AssociatedType {
return 1
}
}
struct SomeStruct2 : SomeProtocol {
var value: Int
func someMethod(value: Int) -> Int {
return 1
}
}
struct SomeStruct3 : SomeProtocol {
struct AssociatedType {}
var value: AssociatedType
func someMethod(value: AssociatedType) -> AssociatedType {
return AssociatedType()
}
}
指定する型とそれを受け継いだサブクラスの話。
それら。
where 句によるprotocolに対するextensionの制御の話。例えば、以下だとWebContentの型を持ったやつ。
protocol Item {
var name: String { get }
}
protocol WebContent {
var url: String { get }
}
extension Item where Self : WebContent {
var description: String {
return "hi"
}
}
struct EBook: Item, WebContent {
let name: String
let url: String
}
struct Book: Item {
let name: String
}
いくつかの標準ライブラリに付与されるプロトロル。
8章
Any関数はダウンキャストが必要。
if let int = something as? Int {
// something
} else {
// else
}
なので、ジェネリクス良いですね。
10章
参照型のクラスと、値型の構造体。その選定基準がとても為になった。
クラスは参照型なので、新しくインスタンスを作ってもそのインスタンスに保持されている値はつながっている。つまるところ、副作用がたくさんあるということですね。一方、structはimmutableに物事を扱うことが可能、と。
class Temp {
var c: Double = 0
}
class Country {
var temp: Temp
init(temp: Temp) {
self.temp = temp
}
}
let t = Temp()
t.c = 25
let c1 = Country(temp: t)
t.c = 40
let c2 = Country(temp: t)
c1.temp.c
c2.temp.c
そのため、クラスは何らかのライフサイクルに添いたいところや参照を共有する必要がある。
11章
OptionalとImplicitlyUnwrappedOptionalの使い分けの話。どう使うべきか、という話。
クロージャを使うときの、キャプチャリストと weak / unowned の違い、その使い分けも。
let closure = { [weak object1, unowned object2] () -> Void in
print(object1)
print(object2)
}
第11章の話の中では、いくつかのデザインパターンの説明も出て来た。これは通知をやり取りするための手法として。その中にObserverパターンがあった。
import Foundation
class Poster {
static let notificationName = Notification.Name("SomeNotification")
func post() {
NotificationCenter.default.post(name: Notification.Name("a"), object: nil)
}
}
class Observer {
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleNotification(_:)),
name: Notification.Name("a"),
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc
func handleNotification(_ notification: Notification) {
print("I got a message")
}
}
var observer = Observer()
let poster = Poster()
poster.post()
12章
非同期処理の話。GCD(Grand Central Dispatch)をSwiftから使う話とか。DispatchQueueの話。Foundationに含まれる Operationを使った話も。
ここら辺はやっぱり複雑なところになるので、中身を調べた上でなにかのライブラリに乗っかりたいですね。
13章
Resultによるエラー処理の話などだった。enumで定義した成功/エラーを switch を使って分けていく方法ですね。
enum Result<T, Error> {
case success(T)
case failure(Error)
}
Erlang/Elixirの {:ok, xxx} や {:error, xxx} に対するパターンマッチなどん処理を思い浮かべました。
毎回 switch を書く必要もありますが、ここは将来パターンマッチ含まれるともっとErlang/Elixirに似た記述になりそうですね。
do-catchはまとめてエラー処理を描きたいときなど
エラー処理の選定
エラー処理を行う --no--> リリース時のプログラムの
| 終了を許容 ----no--> assert
yes `----yes---> fatalError(_:)
|
エラー処理の詳細が必要 ----------------------no--> Optional
|
yes
|
同期処理 ----------------------------------no--> Result
`------------------------------------yes--> do-catch
まとめ
この書籍、やっぱり良いですね。Swiftに関して、言語仕様ではなく経験からどう使うと良いのか、という話がまとまっている。
これを読む/読まないで知識のつけ方が大きく変わりそう。
ありがとうございました。
1 Comment