20200919のSwiftに関する記事は9件です。

iOSアプリのリバースエンジニアリング-[ツール編]

はじめに

この記事は随時追記していきます。時々思い出した頃に見に来てください。ストックかLGTMで多分更新時に通知がいくようにするのでみたい人はお願いします

ツールたち

LLDB

LLVM(Xcodeでも使われているコンパイラ)のプロジェクトの一部として開発された高性能な動的デバッガ。
以下のサイトはgdb to lldbのチートシートであるが、普通にlldbのコマンドを調べるためにも役に立つ。
http://lldb.llvm.org/use/map.html
こちらは純粋なlldbのチートシート
https://www.nesono.com/sites/default/files/lldb%20cheat%20sheet.pdf

breakpointはもちろん、bt(バックトレース)やimage lookup --address、thread returnなどをよく使ってる。
XcodeからでなくコマンドラインでLLDBを使うようになってから、普通のiOSアプリの開発においてもコマンドから使ったりするようになった。

あんまり実用性はないけどGUIも用意されてたりする。
スクリーンショット 2020-09-19 22.07.20.png

otool

いくつかの用法がある。

ライブラリの依存関係を調べる

lddみたいな使い方

otool -L ファイル名

nm

シンボルを抽出して表示する。使用されているクラス名などを調べることができる。

nm ファイル名

strings

ファイルから文字列の部分を抽出する。例えば、pirntf("aaaa");としていれば、aaaaが見つかる。
このコマンドから見つからないようにする方法も今度記事にしようと思っています。

strings ファイル名

dsdump

Hopper Disassembler V4

Ghidra

frida

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSDC2020の HomeKit のセッションが面白かった

iOSDC2020 の HomeKit に関するセッションが面白かったので、セッションで聞いた内容含め簡単にまとめておこうかと思います。暇があったら何か作ってみたい?

HomeKit とは?

image.png

iOS、iPadOS、macOS、watchOS などの Apple製品からデバイスを管理・コントロールできるようにするための IoT アプリケーションの枠組みです。また、そのような HomeKit 対応のアプリケーションを対応する機器をコントロールするアプリとして Apple から Home というアプリが標準で提供されていますが、HomeKitFramework という Framework を使うことで独自にアプリを作ることも可能です。

HomeKit Accessory Protocol(HAP) とは?

HomeKit Accessory Protocol(HAP) は、サードパーティー製アクセサリ(証明やドアロックなど)と Apple製品とが相互に通信するためのプロトコルで、商用目的ではない場合は自由にアクセサリに HAP を実装できますが、商用目的の場合は、MFi プログラムに基づく HomeKit 認定を完了する必要があるみたいです。

Homebridge とは?

Homebridge は、iOS HomeKit API の挙動を HomeKit に対応していないアクセサリを動かすために使用される、ホームネットワークで実行できる軽量のサーバーです。また、それら HomeKit デバイスを制御する Homebridge プラグインが2000以上あるそうです。詳しくはこちら を参照してください。

セッションでは、Homebridge の代表例として、Philips hue の Bridge が消化されていました。この Bridge により他の Philips Hue で提供されているアクセサリーが実質 Home App で使用できるようになっているようです。

https://www.philips-hue.com/ja-jp/p/hue-bridge/8718696562260

HomeKit ADK とは?

HomeKit Accessory DevelopmentKit の略でアクセサリーメーカーが HomeKit に対応したデバイスを作成するための Apple から提供されている開発コードのようです。つまり、前述の Bridge とは違い Home App で直接的にアクセサリを操作できるようにするための DevelopmentKit のようですね。

https://github.com/apple/HomeKitADK

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ViewControllerの画面サイズを変更する

ViewControllerの画面サイズを変更する

StoryBoard上で画面サイズ以上のViewを配置したい時。

1.ViewControllerを選択
2.Size inspector → Simulated Sizeを[Fixed]から[Freeform]に変更
3.WidthとHeightを好きなサイズに変更

以上!

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIButtonの再接続方法

UIButtonの再接続方法

学習を始めた頃は一から接続し直していたので、同じことをしている方に届けたい、、。

1.接続の切れたボタンを、controlキーを押しながら
 再接続したい@IBActionの上に持っていく

2."Connect Action"と出たら離す

以上!

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift版 競プロ用チートシート(初心者向け)

はじめに

こんにちは!屋代高校PC同好会結成会のTARDIGRADEです。
本記事では、Swiftを使って作成した競技プログラミング用チートシートをまとめています。あくまで「そのままコピペして使えるようなチートシートをまとめる」ということが目的ですので、アルゴリズムやデータ構造の仕組みを理解したいという方は、別の記事をご覧ください。
便利そうなものが新しく見つかったら、随時更新していこうと思っているので、「こういうのが欲しいな~」という意見があればコメントください。また、「俺こんなの作ったよ!」というものがあれば、編集リクエストを送っていただければ積極的に採用したいと思います(一応内容の精査はするつもりです)。
Atcoderのコンテストでの提出数を見ても、Swiftで競技プログラミングをやってる方はまだまだ少ないですので、どんどん情報交換していきましょう!

・対象者

Swiftの基本文法(条件分岐、ループ文、関数の作成等)がわかっている方。
本記事では、アルゴリズムやデータ構造をメインに扱うつもりですので、簡単な組み込み関数(sort,abs,max等)についての知識もあるほうが、わかりやすいかと思います。

・実行環境

本記事で作成したすべてのコードは、2020/09/19の時点で、AtCoderのジャッジシステム上で正確に動くことを確認しています。

・注意(必ず読んでください)

いまさら何言ってんだと思われるかもしれませんが、僕はこの記事を書くまで、Swiftにほとんど触れたことがありませんでした。じゃあなんで書こうと思ったんだって話なんですが、それはこの記事の主旨から外れるので省略させてください。一言でいえば友達のためです。
完全独学で経験値もほぼなし、細かい文法なんかは全然抑えられてないです。そのため、普段からSwiftを使って作業されている方から見ると、無駄に長かったり、読みにくかったり、違和感のある書き方をしてしまっているかもしれません。目に余るようでしたら修正しますので、遠慮なく指摘してください。
また、僕は茶コーダーです。難しいアルゴリズムやデータ構造は理解できていない部分もあります。そういう意味で、タイトルにもある通りこの記事は初心者向けということにさせていただいています。
以上の点を理解したうえで、本記事を参考にしたり、あるいは引用していただけるとありがたいです。

標準入力の受け取り方

標準入力は、1行ずつ受け取るのがわかりやすくていいと思います。

// 入力値が一つで、String型として扱いたいとき
// "abc" -> "abc"
let input = readLine()!

// 入力値が一つで、Int型として扱いたいとき
// "6" -> 6
let input = Int(readLine()!)!

// 入力値が複数あり(スペースで区切られている)、String型として扱いたいとき
// "abc" "def" -> ["abc","def"]
let input = readLine()!.split(separator: " ")

// 入力値が複数あり(スペースで区切られている)、Int型として扱いたいとき
// "123" "456" -> [123,456]
let input = readLine()!.split(separator:" ").map{Int($0)!}

// 入力値が一つでString型であるものを、配列として扱いたいとき
// "abcdef" -> ["a","b","c","d","e","f"]
let input = Array(readLine()!).map{String($0)}

mapはとても便利な関数ですので、知らなかった方はぜひ調べてみてください。こちらの記事でも取り上げられています。
Swiftで、とある文字列に対して「指定した位置の文字を取得する」という操作をしようとすると、少々面倒な書き方をしなければなりません(すっきり書く方法もあるのかも…)。ですので、入力された文字列に対して何か操作を行う場合は、一番下にあるように文字列を分割して配列に保存するとよいと思います。

二分探索

二分探索って何?という方はこちらの記事を読んでください。
本記事で作成したものは、正確に言えば「ソート済みの配列aのイテレータのうち、検索したい値以上となる最小のイテレータを返す関数」です。C++でいうところのlower_boundですね。

import Foundation
// 第一引数に二分探索したい配列を渡す
// 第二引数に検索したい要素を渡す
// 第三引数に二分探索したい配列の大きさを渡す
func binarySearch(list:[Int],item:Int,cnt:Int)->Int{
    // 解の存在範囲を初期化
    var low = -1
    var high = cnt 
    // 解の存在範囲が1より大きい間、繰り返す
    while (high - low > 1) {
        let mid = Int(floor(Double(low + high) / 2))
        if list[mid] >= item {
            // midが条件を満たせば、解の存在範囲は(low,mid]
            high = mid
        } else {
            // midが条件を満たさなければ、解の存在範囲は(mid,high]
            low = mid
        }
    }
    // この時点で、low + 1 = highとなっている
    return high
}

binarysearch( list:[2,2,3,5,5,6] , item:5 , cnt:6)なら「3」、binarysearch( list:[1,2,2,2,5] , item:3 , cnt:5)なら「4」を返します。
実際に問題を解く中で二分探索を使ってみたい方は、下の問題を解いてみてください。少し考察が必要で、難易度的にもちょうどいいかと思います。
AtCoder Beginner Contest 077 C - Snuke Festival
Swiftでの解答例はこちら(詳しい解法は公式の解説を見てください)

追加予定のチートシート

bit全探索、DFS、BFS、プライオリティーキュー、セグメント木、Union-Find木

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】@escaping 丸わかり?

@escaping

image.png

@escapingとは?

  • Swiftの 属性(= attribute)の1つ
  • @escaping属性は、クロージャに対して指定する追加情報
  • 関数に引数として渡されたクロージャが、関数のスコープ外で保持される可能性があることを示す

この記事分かりやすいです?

var array = [()->Void]()

func testEscaping(arg: @escaping ()->Void) {
    //関数のスコープ外の配列に追加
    //クロージャが関数外で保持されることになるので、@escaping属性が必要
    array.append(arg)
}
testEscaping {print("玄邪 一郎")}
testEscaping {print("玄邪 二郎")}

array.forEach { $0() } //Swiftではクロージャの引数に自動的に$0、$1、$2…と順に名前が付与されます。

属性(= attribute)とは?

  • コンパイラに対し、宣言や型の補足情報を伝えるもの
  • @を使う。

@escapingを、 使う理由

複数のタスクを、非同期で並列処理させるため。

どんな時に、 必要か

  • クロージャが、スコープ外で強参照されるとき (= プロパティとして保持されるとき)
  • クロージャを、非同期で実行するとき (= メソッド内ですぐに実行されないとき)

こちら参考

クロージャをすぐに実行し、どこからも強参照されない場合は、
@escaping は必要ありません。

よくある例

非同期処理をする、完了ハンドラとしてのクロージャ。

// NG: コンパイルエラー
func someAsyncMethod(completion: () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

// OK
func someAsyncMethod(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}
// completionHandler -> クエリが完了したときに実行される
// DispatchQueue -> 処理待ちタスクを追加するためのキュー

DispatchQueueについて

「循環参照」 に 気を付けよう?

@escapingなクロージャは、どこか から強参照される可能性があります。
その参照元をクロージャ内で強参照すると、循環参照になります...

おしまい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@escaping覚書?【Swift】

 @escaping

image.png

 @escapingとは?

  • Swiftの 属性(= attribute)の1つ
  • @escaping属性は、クロージャに対して指定する追加情報
  • 関数に引数として渡されたクロージャが、関数のスコープ外で保持される可能性があることを示す

 この記事分かりやすいです?

var array = [()->Void]()

func testEscaping(arg: @escaping ()->Void) {
    //関数のスコープ外の配列に追加
    //クロージャが関数外で保持されることになるので、@escaping属性が必要
    array.append(arg)
}
testEscaping {print("玄邪 一郎")}
testEscaping {print("玄邪 二郎")}

array.forEach { $0() } //Swiftではクロージャの引数に自動的に$0、$1、$2…と順に名前が付与されます。

 属性(= attribute)とは?

  • コンパイラに対し、宣言や型の補足情報を伝えるもの
  • @を使う。

 @escapingを、 使う理由

 複数のタスクを、非同期で並列処理させるため。

 どんな時に、 必要か

  • クロージャが、スコープ外で強参照されるとき (= プロパティとして保持されるとき)
  • クロージャを、非同期で実行するとき (= メソッド内ですぐに実行されないとき)

 こちら参考

クロージャをすぐに実行し、どこからも強参照されない場合は、
@escaping は必要ありません。

 よくある例

 非同期処理をする、完了ハンドラとしてのクロージャ。

// NG: コンパイルエラー
func someAsyncMethod(completion: () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

// OK
func someAsyncMethod(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}
// completionHandler -> クエリが完了したときに実行される
// DispatchQueue -> 処理待ちタスクを追加するためのキュー

 DispatchQueueについて

 「循環参照」 に 気を付けよう?

 @escapingなクロージャは、どこか から強参照される可能性があります。
 その参照元をクロージャ内で強参照すると、循環参照になります...

 おしまい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】クロージャー 丸わかり?

image.png

 クロージャ

 使い方

変数引数に、関数の処理を直接代入する。

 使う理由

最近のプログラミング言語では、
引数に関数を入れるとか、戻り値に関数を入れるとかが当たり前になっているので、
そういうときにクロージャを使うと綺麗に記述できるので、みんな嬉しい。

  • 引数として使うパターンが多い

 名前の由来

Closure closes over var.

 コード

 let, varに、関数の処理を直接代入

// MARK: - let, varに、関数の処理を直接代入

// 定数closureが、恰も関数のように扱える
let closure = { () -> () in print("Hello World!")}
closure()

let closure_2 = { () -> Void in print("hamburger")}
closure_2()
// 戻り値がVoid型、引数がない場合は、(引数) -> 戻り値の型 in を省略可
let closure_3 = { print("Yeah!") }
closure_3()

// 型を指定
let closure_4: (Int, Int) -> Void = { (num1: Int, num2: Int) -> Void in print(num1 + num2) }
// 型推論ってやつ
let closure_5 = { (num1: Int, num2: Int) -> Void in print(num1 + num2) }
closure_4(100, 199)
closure_5(100, 199)

// 因みに、closure_4 は num1, num2を省略可。

// 内部引数名を省略 -> 「$」を使用。
// $0 は最初の引数を表し、$1 は2番目の引数を表します。
let closure_6: (Int, Int, Int, Int) -> Void = {
    print ($0 - $1 + $2 + $3)
}
closure_6(30, 20, 100, 10) // 120

 型推論とは?

型の指定をしなくても、代入した値に応じて値の型を推論してくれる機能。
つまり、Swiftは変数の宣言時に型の指定を省略できる。( -> むしろ推奨。)


 クロージャを引数として、関数を実行

  • 引数として使うパターンが多い
// MARK: - クロージャを引数として、関数を実行

func closureTest(num1: Int, num2: Int, closure: (Int, Int) -> Int) { print(closure(num1, num2)) }

// return文が1行のみの場合には「return」は省略可
closureTest(num1: 300, num2: 5000, closure: { (num1, num2) -> Int in return num1 + num2 })
closureTest(num1: 300, num2: 5000, closure: { (num1, num2) -> Int in num1 + num2 })

// 引数の型を入力しないと、こうなるので注意。
// closureTest(<#T##<<error type>>#>, <#T##<<error type>>#>, <#T##<<error type>>#>)

// トレーリング クロージャ (= Trailing Closure)
closureTest(num1: 100, num2: 400) { (num1, num2) -> Int in
    num1 + num2 // return省略
}

 Trailing Closureとは?

関数の引数のうち 最後の引数がクロージャの場合、
クロージャを( )の外に書くことができる。

func testprint(str1: String, closure: (String) -> Void) {
    closure("僕の名前は\(str1)です。")
}

// トレーリングクロージャの場合 -> 美しい
testprint(str1: "玄邪 太郎") { string in
    print(string)   
}

// 通常のクロージャの場合 -> 可読性が悪い...
testprint(str1: "玄邪 太郎",{ string in
    print(string)   
})
  • クロージャが引数だと、{ }の外に( )を包まないといけないので、 可読性が悪くなる。(-> そこで、Trailing Closureが考案)

 クロージャの、基本的な性質2つ

  • ?クロージャーは関数の引数変数として使える
  • ?クロージャーは自分が定義されたスコープをキャプチャする

【Swift】クロージャの理解

 引数としてのクロージャ、利点?

  • コードが綺麗になる。
  • 呼び出し側で、処理が記述できる

呼び出され側は、クロージャに引数として値を渡し、
「値は渡すから、その値はそちらで好きに料理してね」という具合です。

class Foo {
    let val:Int = 10

    func testClosure(closure: (Int) -> Void) {
        closure(self.val) // self
    }
}

class Bar {
    let foo = Foo()

    // 引数の値を倍にする
    func twice() {
        foo.testClosure{ arg in print(arg * 2) } // arg = argument = 引数
    }

    // 引数の値を半分にする
    func half() {
        foo.testClosure{ arg in print(arg / 2) }
    }
}

let bar = Bar()
bar.twice()    // 20
bar.half() // 5

 クロージャによる、 変数と定数のキャプチャ?

  • ローカルスコープ(= scope_1)で定義された変数や定数は、
    ローカルスコープ内でしか使用できませんが、(= scope_2では使用不可)

  • クロージャが参照している変数や定数は、
    クロージャが実行されるスコープ(= scope_4)が
    変数や定数が定義されたローカルスコープ以外(= scope_3以外)であっても、
    クロージャの実行時に使用できます。

これは、クロージャが 自身の定義されたスコープ(= scope_3) の
 変数や定数への参照を保持している為で、この機能をキャプチャと言います。

class Foo {
    // scope_2関数は、普通の関数scope_1を2回実行しています。
    func scope_1() {
        var toto = 1
        toto += 1 // toto = toto + 1 と同義 (= 変数totoの値を更新)
        print(toto)
    }

    func scope_2() {
        scope_1()
        scope_1()
    }

    // scope_4関数は、scope_3関数から返されたクロージャを2回実行しています。
    func scope_3() -> () -> Void {
        var tete = 10
        let closure = { tete += 1
            print(tete)
        }
        return closure
    }

    func scope_4() {
        let tutu = self.scope_3()
        tutu()
        tutu()
    }
}

let test = Foo()
test.scope_2() // 2, 2
test.scope_4() // 11, 12

// scope_2関数は、普通の関数scope_1を2回実行しています。

普通の関数は、実行後はリセットされるので、
1回目の実行で「2」と表示されても、実行後ローカル変数totoの値は「1」に戻ります。

なので2回目の実行でも「2」と表示されます。

// scope_4関数は、scope_3関数から返されたクロージャを2回実行しています。

クロージャが、scope_3関数のローカル変数teteを参照しているので、
1回目の実行で「11」、2回目の実行では1回目の参照を保持しているので「12」となります。


 キャプチャとは?

 自身の定義されたスコープの変数や定数への、参照を「保持」する機能。
 クロージャ特有の機能。

 クロージャにて、self を使う理由

 明示的なselfには、循環参照 が存在しないよう確認を促す役割がある。

(Escaping Closuresは)循環参照を起こす危険性があるので、
(プログラマにそれを意識させるために)クロージャーの中で親スコープを強参照するときは、selfをつける。

selfを付けることが、循環参照を防ぐことに直結しているわけではなくて
コードの意図が明確になり、循環参照しないかにプログラマが意識を向けて書くようになる。

 おしまい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロージャー覚書?【Swift】

image.png

 クロージャ

 使い方

変数引数に、関数の処理を直接代入する。

 使う理由

最近のプログラミング言語では、
引数に関数を入れるとか、戻り値に関数を入れるとかが当たり前になっているので、
そういうときにクロージャを使うと綺麗に記述できるので、みんな嬉しい。

  • 引数として使うパターンが多い

 名前の由来

Closure closes over var.

 コード

 let, varに、関数の処理を直接代入

// MARK: - let, varに、関数の処理を直接代入

// 定数closureが、恰も関数のように扱える
let closure = { () -> () in print("Hello World!")}
closure()

let closure_2 = { () -> Void in print("hamburger")}
closure_2()
// 戻り値がVoid型、引数がない場合は、(引数) -> 戻り値の型 in を省略可
let closure_3 = { print("Yeah!") }
closure_3()

// 型を指定
let closure_4: (Int, Int) -> Void = { (num1: Int, num2: Int) -> Void in print(num1 + num2) }
// 型推論ってやつ
let closure_5 = { (num1: Int, num2: Int) -> Void in print(num1 + num2) }
closure_4(100, 199)
closure_5(100, 199)

// 因みに、closure_4 は num1, num2を省略可。

// 内部引数名を省略 -> 「$」を使用。
// $0 は最初の引数を表し、$1 は2番目の引数を表します。
let closure_6: (Int, Int, Int, Int) -> Void = {
    print ($0 - $1 + $2 + $3)
}
closure_6(30, 20, 100, 10) // 120

 型推論とは?

型の指定をしなくても、代入した値に応じて値の型を推論してくれる機能。
つまり、Swiftは変数の宣言時に型の指定を省略できる。( -> むしろ推奨。)

 内部引数とは?

 関数を呼び出すときは、外部引数名を利用します。

 普段 私たちは、内部引数名外部引数名として、関数を呼び出しています。
 違いはこちら


 クロージャを引数として、関数を実行

  • 引数として使うパターンが多い
// MARK: - クロージャを引数として、関数を実行

func closureTest(num1: Int, num2: Int, closure: (Int, Int) -> Int) { print(closure(num1, num2)) }

// return文が1行のみの場合には「return」は省略可
closureTest(num1: 300, num2: 5000, closure: { (num1, num2) -> Int in return num1 + num2 })
closureTest(num1: 300, num2: 5000, closure: { (num1, num2) -> Int in num1 + num2 })

// 引数の型を入力しないと、こうなるので注意。
// closureTest(<#T##<<error type>>#>, <#T##<<error type>>#>, <#T##<<error type>>#>)

// トレーリング クロージャ (= Trailing Closure)
closureTest(num1: 100, num2: 400) { (num1, num2) -> Int in
    num1 + num2 // return省略
}

 Trailing Closureとは?

関数の引数のうち 最後の引数がクロージャの場合、
クロージャを( )の外に書くことができる。

func testprint(str1: String, closure: (String) -> Void) {
    closure("僕の名前は\(str1)です。")
}

// トレーリングクロージャの場合 -> 美しい
testprint(str1: "玄邪 太郎") { string in
    print(string)   
}

// 通常のクロージャの場合 -> 可読性が悪い...
testprint(str1: "玄邪 太郎",{ string in
    print(string)   
})
  • クロージャが引数だと、{ }の外に( )を包まないといけないので、 可読性が悪くなる。(-> そこで、Trailing Closureが考案)

 クロージャの、基本的な性質2つ

  • ?クロージャーは関数の引数変数として使える
  • ?クロージャーは自分が定義されたスコープをキャプチャする

【Swift】クロージャの理解

 引数としてのクロージャ、利点?

  • コードが綺麗になる。
  • 呼び出し側で、処理が記述できる

呼び出され側は、クロージャに引数として値を渡し、
「値は渡すから、その値はそちらで好きに料理してね」という具合です。

class Foo {
    let val:Int = 10

    func testClosure(closure: (Int) -> Void) {
        closure(self.val) // self
    }
}

class Bar {
    let foo = Foo()

    // 引数の値を倍にする
    func twice() {
        foo.testClosure{ arg in print(arg * 2) } // arg = argument = 引数
    }

    // 引数の値を半分にする
    func half() {
        foo.testClosure{ arg in print(arg / 2) }
    }
}

let bar = Bar()
bar.twice()    // 20
bar.half() // 5

 クロージャによる、 変数と定数のキャプチャ?

  • ローカルスコープ(= scope_1)で定義された変数や定数は、
    ローカルスコープ内でしか使用できませんが、(= scope_2では使用不可)

  • クロージャが参照している変数や定数は、
    クロージャが実行されるスコープ(= scope_4)が
    変数や定数が定義されたローカルスコープ以外(= scope_3以外)であっても、
    クロージャの実行時に使用できます。

これは、クロージャが 自身の定義されたスコープ(= scope_3) の
 変数や定数への参照を保持している為で、この機能をキャプチャと言います。

class Foo {
    // scope_2関数は、普通の関数scope_1を2回実行しています。
    func scope_1() {
        var toto = 1
        toto += 1 // toto = toto + 1 と同義 (= 変数totoの値を更新)
        print(toto)
    }

    func scope_2() {
        scope_1()
        scope_1()
    }

    // scope_4関数は、scope_3関数から返されたクロージャを2回実行しています。
    func scope_3() -> () -> Void {
        var tete = 10
        let closure = { tete += 1
            print(tete)
        }
        return closure
    }

    func scope_4() {
        let tutu = self.scope_3()
        tutu()
        tutu()
    }
}

let test = Foo()
test.scope_2() // 2, 2
test.scope_4() // 11, 12

// scope_2関数は、普通の関数scope_1を2回実行しています。

普通の関数は、実行後はリセットされるので、
1回目の実行で「2」と表示されても、実行後ローカル変数totoの値は「1」に戻ります。

なので2回目の実行でも「2」と表示されます。

// scope_4関数は、scope_3関数から返されたクロージャを2回実行しています。

クロージャが、scope_3関数のローカル変数teteを参照しているので、
1回目の実行で「11」、2回目の実行では1回目の参照を保持しているので「12」となります。


 キャプチャとは?

 自身の定義されたスコープの変数や定数への、参照を「保持」する機能。
 クロージャ特有の機能。

 クロージャにて、self を使う理由

 明示的なselfには、循環参照 が存在しないよう確認を促す役割がある。

(Escaping Closuresは)循環参照を起こす危険性があるので、
(プログラマにそれを意識させるために)クロージャーの中で親スコープを強参照するときは、selfをつける。

selfを付けることが、循環参照を防ぐことに直結しているわけではなくて
コードの意図が明確になり、循環参照しないかにプログラマが意識を向けて書くようになる。

 おしまい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む