SwiftのTipsを書きます。
追記
Tips その2にするつもりだったものを投稿しました。
Tipsっぽくなくなってしまったので当記事のタイトルからその1を外しました。
クロージャのメモリ管理について
enum
Associated values enum
例えば下記のようなC言語の構造体と同じ意味合いのデータ構造はSwiftだと型情報と値をまとめられます。
enum Type {
UNDEFINED,
BOOLEAN,
NUMBER,
STRING
}
struct Variant {
Type type;
int boolval;
double floatval;
char * stringval;
}
enum Variant {
case Bool(Swift.Bool)
case Number(Double)
case String(Swift.String)
}
Result
よく使われるResult型はこれとジェネリクスを組合せて、成否とそれに紐づく任意の情報をまとめています。
enum Result<T, U:ErrorType> {
case Success(T)
case Failure(U)
}
参考:Result.swift - GitHub Alamofire
struct
組合せ可能なオプション(OptionSetType)
struct MyOption: OptionSetType {
private(set) var rawValue: UInt
init(rawValue: UInt) { self.rawValue = rawValue }
static let OptionA = MyOption(rawValue: 1)
static let OptionB = MyOption(rawValue: 2)
static let OptionC = MyOption(rawValue: 4)
}
// OptionA と OptionC の組合せ
let opt: MyOption = [.OptionA, .OptionC]
typealias
別名をつける
例えば何かしらの処理の完了時に呼び出されるクロージャがある場合、typealias
を使うと下記のように書けます。
typealias CompleteHandler = (String, NSError) -> Void
func hoge(completion: CompleteHandler) {
// ...
}
func fuga(completion: CompleteHandler) {
// ...
}
typealiasを使わないとこうなります。引数名で意味は通りますが、
後から"クロージャの第二引数をOptionalにしたい"と思った時に変更漏れを起こすかもしれません。
func hoge(completion: (String, NSError) -> Void) {
// ...
}
func fuga(completion: (String, NSError) -> Void) {
// ...
}
クロージャに限らず型に意味を持たせられる時は typealias
で別名を与えましょう。
Associated Types
下記のプロトコルを見てください。
typealias
を定義していますがどの型にも紐づけられていません。
protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
これを関連型(Associated Types)といい、プロトコルを実装するクラスで紐づけを行います。
この時、Genericを使うこともできます。もちろん使わなくてもいいです。
typealias ItemType
はどこいったの?と思いますがItemTypeが使われていた箇所を
すべて型指定に変えることで省略可能です。
struct Stack<Element>: Container {
// original Stack<Element> implementation
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
何が嬉しいのかですが、型の制約を作れます。
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, _ anotherContainer: C2) -> Bool {
// 省略...
}
SequenceType
プロトコルなどで使われているので知っておくと実装する時に捗ります。
参照:Associated Types ーThe Swift Programming Language
String
文字列を複数行に分けて書く
let text = "hoge\nfuga\nfoo\nbar"
let text = [
"hoge",
"fuga",
"foo",
"bar",
].joinWithSeparator("\n")
デバッグモードの時だけ出力する
func print(items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(items[0], separator:separator, terminator: terminator)
#endif
}
デバッグモードのみで有効になる DEBUG
の定義が必要です。
終端文字の指定
引数 terminator:
で指定します。
// 改行させない
print("HOGE", terminator: "")