LoginSignup
20
21

More than 5 years have passed since last update.

逆引き Swift

Last updated at Posted at 2014-09-02

Swift メモ改め、逆引き Swift になりました。
「アレ、Swift ではどうやるの?」
「Swift だとこんなことができる!」
をまとめていきたいと思います。

NSArrayNSDictionaryfor in ループで使用するには

各要素の型が同一であれば ArrayDictionary にキャストできる。

for_in_array.swift
for subview in view.subviews as [UIView] {
    ...
}
for_in_dictionary.swift
for (key, value) in map as [String: Int] {
    ...
}

self のみ変更可能な readonly プロパティを定義するには

private_settable_public_gettable.swift
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!!

プロパティの初期化をカスタムロジックで行うには

下記のように、遅延初期化を単純なコンストラクタ呼び出しだけでなく、独自のロジックを実行したい場合

lazy_prop.m
@implementation Foo
- (Bar*)bar {
    static Bar* bar;
    if (!bar) {
        bar = [Bar new];
        ...独自の初期化処理...
    }
    return bar;
...
@end

Swift では以下のようにクロージャを利用した方法がある。

lazy_prop.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 型宣言を丸カッコで囲めばよい。

closure_prop.swift
class Foo {
    var completionHandler: ((error: NSError?) -> Void)?

    ...
}

typealias を使って Closure 型を定義しておくと読みやすい。

closure_prop_using_typealias.swift
typealias CompletionHandler = (error: NSError?) -> Void


class Foo {
    var completionHandler: CompletionHandler?

    ...
}

クラスにスタティックプロパティを定義するには

Swift は今のところ、クラスに実体を擁するスタティックプロパティを定義することができない。
(計算済みプロパティは定義可能)

Swift 1.2 よりサポートされるようです!

クラスインスタンス間で値を共有したい場合、内部構造体を利用することで相当のことが行える。

class_prop.swift
class Foo {
    // クラス間で共有するスタティックプロパティを内部構造体内に定義する
    private struct DefaultData {
        static var body: NSData = NSData()
    }

    class func setDefaultData(data: NSData) {
        DefaultData.body = data    // 共有プロパティの参照と更新
    }
}

シングルトンを実装するには

Swift にスタティックプロパティの制限があることから、上記の手法の応用でシングルトンが実装できる。

singleton.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 することはできないので、プログラムの実行継続が困難な「不測の事態」の表現として扱うのが妥当と考えられる。

exception.swift
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 は使用できないが、メソッド名を文字列で表現すればよい。

method_as_selector.swift
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 という、クラスを参照するプロパティが存在する。
これを用いて、クラスメソッドを参照して呼び出すことが可能。

classmethod_from_inst.swift
    self.dynamicType.klassMethod()

クロージャをシンプルに実装するには

おなじみのロジック

dispatch_in_main.swift
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    ...UI の更新など...
})
  • クロージャの戻り値が Void の場合 -> Void は省略できる。
  • クロージャの引数型リスト (...) in は省略できる。
  • メソッドの最後の引数がクロージャ(ブロック)の場合、Ruby の do - end 構文のような書き方ができる。
  • メソッドの引数が Void の場合、 () は省略できる。

以上を利用すると、次のように書くことができる。

dispatch_in_main2.swift
// メインスレッドで実行する 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 に対応したドキュメントを記述するには

documentable.swift
/**
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 が使えないので、ループしたいときは以下のように。

enumerate_set.swift
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 という名前で参照できる
sort_using_closure.swift
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 演算子でプロトコルに準拠しているか調べることができる
  • 比較演算子 === !== が利用できる
20
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
21