Swift メモ改め、逆引き Swift になりました。
「アレ、Swift ではどうやるの?」
「Swift だとこんなことができる!」
をまとめていきたいと思います。
NSArray
や NSDictionary
を for in
ループで使用するには
各要素の型が同一であれば Array
や Dictionary
にキャストできる。
for subview in view.subviews as [UIView] {
...
}
for (key, value) in map as [String: Int] {
...
}
self
のみ変更可能な readonly
プロパティを定義するには
class Foo {
private (set) var value: Int
init(value: Int) {
self.value = value
}
...
}
...
let foo = Foo(100)
let v = foo.value // OK
foo.value = 200 // NG!!
プロパティの初期化をカスタムロジックで行うには
下記のように、遅延初期化を単純なコンストラクタ呼び出しだけでなく、独自のロジックを実行したい場合
@implementation Foo
- (Bar*)bar {
static Bar* bar;
if (!bar) {
bar = [Bar new];
...独自の初期化処理...
}
return bar;
...
@end
Swift では以下のようにクロージャを利用した方法がある。
class Foo
{
private (set) lazy var bar: Bar = {
var bar = Bar()
...独自の初期化処理...
return bar
}()
...
}
...
let foo = Foo() // lazy により、この時点で foo.bar は生成されていない
let bar = foo.bar // このタイミングで bar を生成するクロージャを実行
クロージャをプロパティとして定義するには
コールバックをオプションで提供したい場合などに用いる方法だが、普通の Closure 型宣言を丸カッコで囲めばよい。
class Foo {
var completionHandler: ((error: NSError?) -> Void)?
...
}
typealias
を使って Closure 型を定義しておくと読みやすい。
typealias CompletionHandler = (error: NSError?) -> Void
class Foo {
var completionHandler: CompletionHandler?
...
}
クラスにスタティックプロパティを定義するには
Swift は今のところ、クラスに実体を擁するスタティックプロパティを定義することができない。
(計算済みプロパティは定義可能)
Swift 1.2 よりサポートされるようです!
クラスインスタンス間で値を共有したい場合、内部構造体を利用することで相当のことが行える。
class Foo {
// クラス間で共有するスタティックプロパティを内部構造体内に定義する
private struct DefaultData {
static var body: NSData = NSData()
}
class func setDefaultData(data: NSData) {
DefaultData.body = data // 共有プロパティの参照と更新
}
}
シングルトンを実装するには
Swift にスタティックプロパティの制限があることから、上記の手法の応用でシングルトンが実装できる。
class Manager {
class var sharedManager: Manager {
struct Singleton {
static let body = Manager() // sharedManager 参照時にインスタンス化される
}
return Singleton.body
}
}
...
let manager = Manager.sharedManager
#pragma mark
を行うには
はじまりが MARK:
のコメントが #pragma mark
として扱われる。
// MARK: - UITableViewDelegate implementation
override func numberOfSectionsInTableView(tableView:UITableView) -> Int {
return 1
}
...
例外を投げるには
そもそも Swift は例外をサポートしていない。
ただ NSException
を使って例外を送出することはできる。
Swift 内で catch することはできないので、プログラムの実行継続が困難な「不測の事態」の表現として扱うのが妥当と考えられる。
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
http://oropon.hatenablog.com/entry/2014/06/07/022319
http://stackoverflow.com/questions/24010569/error-handling-in-swift-language
メモ: 例外を ARC モードで使用するとメモリリークが生じるため、使用が推奨されていない。
セレクタに Swift のメソッドを指定するには (@selector)
@selector
は使用できないが、メソッド名を文字列で表現すればよい。
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: "timerDidTimeout:", userInfo: nil, repeats: true)
}
internal func timerDidTimeout(timer: NSTimer!) {
...
}
インスタンスからクラスの参照を得るには (self.class)
Swift のクラスインスタンスには dynamicType
という、クラスを参照するプロパティが存在する。
これを用いて、クラスメソッドを参照して呼び出すことが可能。
self.dynamicType.klassMethod()
クロージャをシンプルに実装するには
おなじみのロジック
dispatch_async(dispatch_get_main_queue(), { () -> Void in
...UI の更新など...
})
- クロージャの戻り値が
Void
の場合-> Void
は省略できる。 - クロージャの引数型リスト
(...) in
は省略できる。 - メソッドの最後の引数がクロージャ(ブロック)の場合、Ruby の
do - end
構文のような書き方ができる。 - メソッドの引数が
Void
の場合、()
は省略できる。
以上を利用すると、次のように書くことができる。
// メインスレッドで実行する dispatch_async()
func dispatch_async_main(block: dispatch_block_t!) {
dispatch_async(dispatch_get_main_queue(), block)
}
...
dispatch_async_main {
...UI の更新など...
}
DEBUG ビルドと RELEASE ビルドでロジックを変更するには
Objective-C 同様 #if DEBUG ... #end
構文が使えるのだが、
Xcode 6.1 だとなぜか Swift のコンパイル時に DEBUG マクロが定義されていないようなので、プロジェクトの設定を変更する。
- xcodeproj の Other Swift Flags (Debug) に
-D DEBUG=1
を追記。
Quick Help に対応したドキュメントを記述するには
/**
2 つの値の和を求める
:param: a 被加算値.
:param: b 加算値.
:returns: 和を返す.
*/
func add(a: Int, b: Int) -> Int {
...
}
VVDocumenter という Xcode plug-in を導入するのが手っ取り早い。
その他、Objective-C との違いなど
Swift 標準の集合型(Set)が無い
Swift 1.2 より Set がサポートされるようです!
NSSet
で代用。for in
が使えないので、ループしたいときは以下のように。
set.enumerateObjectsUsingBlock { (_value, _) -> Void in
let value: String = _value as String // TODO: 引数で直接 String として受け取ることはできない?
println("value=\(value)")
}
プロパティ
- タイププロパティ(static なプロパティ)のサポート。
- 遅延初期化 (lazy) の追加。
- オブザーバ (willSet, didSet) の追加。
- 属性指定に代わる諸仕様。
- class でない型(struct, enum) の値の代入は copy として振る舞う。
- 弱参照(weak) は weak キーワードで指定。
- readonly は let 指定または get だけを実装。
- インスタンス変数の直接参照や直接変更はできない。
オプショナル型(?
, !
)
型のうしろに ?
や !
を付けるとオプショナル型となり、nil
状態を持てるようになる。
-
?
を付けたオプショナル型は、中の値を参照する場合、変数名の末尾に!
を付けなければならない。 -
!
を付けたオプショナル型は、上記と異なり中の値を自動で展開する。
var optionalValue1:Int? = 100
let contentValue1 = optionalValue1! // 中の値の取り出しには ! をつける
optionalValue1 = nil
var optionalValue2:Int! = 200
let contentValue2 = optionalValue2
optionalValue2 = nil
クロージャ(Blocks)
{(引数名:型名,...) -> 戻り値型名 in
コードブロック...
}
- 型名は省略できる
- 不要な引数は
_
で省略できる - 引数を名前指定を受け取る代わりに
$0
$1
という名前で参照できる
strings.sortUsingComparator( { (_a, _b) -> NSComparisonResult in
let a: NSString = _a as NSString
let b: NSString = _b as NSString
return a.compare(b)
})
プロトコル
@objc
を付けることで Objective-C のプロトコルと互換性を持たせられるだけでなく、いくつかの制限が緩和される(Swift 1.1)。
-
optional
なメソッドが宣言できる -
as
演算子でプロトコルに準拠しているか調べることができる - 比較演算子
===
!==
が利用できる