トップ 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|

2018-10-15 [cocoa][swift]え!それって参照渡し?

とても違和感を感じる用語がある。それは、『参照渡しだ』。

え!違うだろ?と感じていたが、いい機会なので、調べてみた。

引数

引数と呼ばれるものにどんな種類があるのか、まずは列挙してみる。

実引数

関数に渡す値。

sum(1, 2);

仮引数

関数が受け取った変数。

int sum(int a, int b)
{
    return a + b;
}

値渡し(call by value)

値が渡される。

_ <h2>ポインタ渡し

渡す値がアドレスの値渡し。

参照渡し

  • 変数渡し(call by variable)
    変数そのものを渡す。
  • 参照渡し(call by reference)
    参照渡しで、内部でアドレス情報を渡す方法。

Pascal

Pascalでは,値渡し(call by value)と変数渡し(call by variable)が存在し、変数渡しは参照渡しに相当する。

そもそもは、変数渡しの実装方法に参照渡しがある。参照渡しは、変数に対する参照(アドレス情報)を渡す方法だ。

Inside Macintoshは、コードはPascalで記載されているが、それをC言語で利用する場合、varがついた変数渡しの引数は、C言語ではポインターと読み替えていた。

具体的には、Inside Macintoshで以下のようにPascalで説明されていたとする。

PROCEDURE GetPort(VAR port: GrafPtr);

これをC言語では、以下のように読み替える。

void GetPort (GrafPtr * port);

C言語

C言語の関数の引数は全て値渡し。K&Rでしっかりと説明されている。

ポインタでアドレスの値を渡すのを参照渡しと呼ぶのは間違いだ。

Java

Javaも全て値渡し。

ポインタ演算ができない、アドレス(参照)の値渡しが利用できるが、これを参照渡しと呼ぶのは、如何なものか。

C++

値渡しに加え、本物の参照渡しが存在する。

void time_two(int& a)
{
    a *= 2;
}

また、C++11では右辺値参照・ムーブセマンティクスという所有者の移動が用意されている。

C#

二つの参照渡しの方法が用意されている。

int initializeInMethod = 0;  // 初期化が必須
OutArgExample(initializeInMethod);
Console.WriteLine(initializeInMethod);
 
void OutArgExample(ref int number)
{
    number = 44;
}
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod);
 
void OutArgExample(out int number)
{
    number = 44;  // 代入が必須
}

refは変数が渡されるので、初期化によって値が設定されていない変数はNGだ。

outは値を返すという意味から、初期化は必須でないか、関数内で値を必ず設定しないといけない。

Swift

値渡しだが、inoutキーワードをつけると値呼びの結果返し(call-by-value-result)となる。

C言語のポインターの場合と同様に、変数が参照型の場合、参照の値渡しにより値を変更できる。

class Demo {
    public var num = 0;
}
 
func set999(a: Demo) {
    a.num = 999;
}
 
let demo = Demo()
set999(demo)

値呼びの結果返しの例。

func set999(a: inout Int) {
    a =999
}
 
var num = 0
set999(&num)

2018-10-10 [cocoa][swift]プライバシーとセキュリティ(リモート通知のデバイストークンの扱いなど)

GDPRで話題になる頻度が上がったプライバシーとセキュリティ。対応は個々でだと思うが、サーバを利用しない方が難しい今のスマートフォン・アプリケーション開発者にとって、システム提供側によって、何らかの対応が必要になる可能性がある。そこで、今回の発表では、iOSアプリケーション開発に関係がありそうな項目について取り上げてみた。

GDPRとは

対象は個人情報。内容は、思いっきり端折ると、利用目的など、同意を得ないといけないのと、EEAで得られた情報を第三國へ移転するのは禁止だ。

Appleは

Data and Privacy(https://privacy.apple.com/)というページが用意されているが、これはApple製品向けで、サードパーティの開発者向けの情報でない。

iOS 11.3 で、GDPR相当のプライバシー保護機能を搭載したので、各アプリ/サービスは、個々で判断して対応しろということだろう。

Googleは

GoogleはFirebaseサービスを利用した開発者向けに、Firebase のプライバシーとセキュリティ(https://firebase.google.com/support/privacy/?hl=ja)というページを用意している。

このページでは、Googleの対応内容と開発向けについて説明がある。我々アプリケーション開発者が関係する後者について、ざっと抜き出してみる。

Firebaseの個々のサービスで扱い個人情報は、以下のとおり。

Firebaseサービス 個人情報
Cloud Functions for Firebase IP addresses
Firebase Authentication Passwords
Email addresses
Phone numbers
User agents
IP addresses
Firebase Cloud Messaging Instance IDs
Firebase Crash Reporting Instance IDs
Crash traces
Firebase Crashlytics
Firebase Dynamic Links Device specs (iOS)
Firebase Hosting IP addresses
Firebase Invites Device specs (iOS)
Locally-stored contacts
Firebase Performance Monitoring Instance IDs
IP addresses
Firebase Predictions Instance IDs
Firebase Realtime Database IP addresses
User agents
Firebase Remote Config Instance IDs
Google Analytics for Firebase Mobile ad IDs
IDFVs/Android IDs
Instance IDs
Analytics App Instance IDs
ML Kit for Firebase Uploaded Images

以下について、開発者の対応方法が説明されている。

  • Cloud Messaging
    自動初期化を禁止する(Android)または自動初期化を禁止する(iOS)
    https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja#prevent-auto-init
    https://firebase.google.com/docs/cloud-messaging/ios/client?hl=ja#prevent-auto-init
  • Crashlytics
    オプトイン レポートを有効にする
    https://firebase.google.com/docs/crashlytics/customize-crash-reports?hl=ja#enable_opt_in_reporting
  • Crash Reporting
    オプトイン レポートを有効にする
    https://firebase.google.com/docs/crash/disable-sdk?hl=ja
  • Performance Monitoring
    オプトイン モニタリングを有効にする
    https://firebase.google.com/docs/perf-mon/disable-sdk?hl=ja
  • アナリティクス
    アナリティクスの収集を無効にする
    https://firebase.google.com/support/guides/disable-analytics?hl=ja

この中から、iOSと関係が深いと考えたものについて、もう少し深く調べてみる。

Firebase Cloud Message (FCM) は、iOSのリモート通知に似た位置付けのサービスだ。FCMはインスタンスIDを生成し、それを登録トークンとして利用する。このインスタンスIDが、iOSリモート通知のデバイス・トークンに相当する。このインスタンスIDはアプリケーションをインストールした後にアプリケーションを起動すると自動で生成される。

この良かれと考えて用意した機能が仇になったのか、自動初期化を禁止する方法が用意された。AndroidManifest.xmlに以下の設定を行うと、自動初期化は禁止となる。

<?xml version="1.0" encoding="utf-8"?≶
<application≶
  <meta-data android:name="firebase_messaging_auto_init_enabled"
             android:value="false" /≶
</application≶

例えば、ユーザにクラウド・メッセージの利用を確認してOKとなった場合、以下のコードで自動初期化は有効となる。

FirebaseMessaging.getInstance().setAutoInitEnabled(true);

この有効/無効の設定はシステムによって保持されるので、例えば、ユーザへの確認結果を記録する方法として利用できると考えられる。

ちなみに、現在のインスタンスIDは以下のコードで取得することができる。

FirebaseInstanceId.getInstance().getToken()

これについて、動作確認をして分かったことがあるのだが、正しいのか自信がないため、発表資料では説明しないので、興味がある方は、勉強会で質問してほしい。

上記は、Firebase Cloud Messageの場合だが、iOSのリモート通知のデバイス・トークンも個人情報として、同様な考えて扱うべきだと考えられるので、Firebaseの情報は参考になると思う。

Firebaseはクロスプラットフォームなサービスなので、iOSアプリケーションでも利用している場合があると思うが、今回の件で気をつけないといけないことがある。

Firebaseのアナリティックスは明示的に利用するコードを書かなくても、アナリティックスの収取を行なっている。また、広告IDの収集も行なっている。

Androidで収集を完全に無効にする場合は、AndroidManifest.xmlに以下の設定を行う。

<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" /≶

Androidで広告IDの収集を無効にする場合は、AndroidManifest.xmlに以下の設定を行う。

<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" /≶

iOSの場合、Info.plistにFIREBASE_ANALYTICS_COLLECTION_DEACTIVATEDをYESで定義すると収集が完全に無効になる。そして収集を再び有効にするには、この定義を削除すればいいみたいだ


2018-10-01 [cocoa][swift]MojaveのDark Mode

Macintoshが革新的だったのは、白色背景に黒文字を表示したことだ。これは、macOSのAquaにも受け継がれたのだが、MojaveのDark Mode登場により状況が変わった。説明を信じれば集中したいときはDark Modeが適しているそうだ。

真偽はさておいて、Dark Modeに試してみて感じるのは、Dark Modeに完全対応していないアプリケーションが存在すると、画面が見苦しくなるということ。

今回の発表では、Dark Modeへの対応方法を駆け足で紹介してみる。

lightとdark

システム環境設定で、従来のlightとdarkは切り替えらる。ということは、切り替えに追従して表示を変更できるようにしないといけない、ということになる。

設定

UI Element Colors

ユーザインタフェースに使う色を直接記述してしまうと外観モードの切り替えに対応できないのは当然だ。NSColorではユーザインタフェースのために以下のような色指定を用意している。

  • labelColor
  • controlColor
  • controlBackgroundColor

また、Color Set assetを独自に定義できる。

asset

これを以下のように利用する。

let aColor = NSColor(named: NSColor.Name("MyColor"))

以下は、色の定数を利用した例だ。

override func updateLayer() {
   self.layer?.backgroundColor = NSColor.textBackgroundColor.cgColor
}

2018-09-15 [cocoa][swift]AppleScriptとは何ぞや

スマートフォン・エンジニアにとって、macOSはOfficeが動くUNIXという利点があり、シェル・スクリプトを利用した自動化に威力を発揮している。ただ、macOSにはMacintosh時代からのAPpleScriptがあり、これを利用しない手はない。
自動化したけど、アラートが表示されていて失敗したという悲しいトラブルも、これで回避できるかも!?

シェルスクリプトとして実行する

#!/usr/bin/osascript
display dialog "Welcome to AppleScript."

シェルスクリプトからAppleScriptをよぶ

#!/bin/sh
osascript -e 'display dialog "Welcome to AppleScript."'

AppleScriptからシェルスクリプトを呼ぶ

set fileInfo to do shell script "cd ~; ls" 
display dialog fileInfo 

最前面のアプリを終了させる

#!/usr/bin/osascript
 
tell application "System Events"
    set fullname to name of (path to frontmost application)
end tell
 
set savedDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
set front_app to items 1 thru -2 of text items of fullname as text
set AppleScript's text item delimiters to savedDelimiters
 
if front_app is "Firefox" then
    tell application "Firefox"
        quit
    end tell
end if

最前面のアプリのダイアログを改行キーで閉じる

#!/usr/bin/osascript
 
tell application "System Events"
    set fullname to name of (path to frontmost application)
end tell
 
set savedDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
set front_app to items 1 thru -2 of text items of fullname as text
set AppleScript's text item delimiters to savedDelimiters
 
if front_app is "Firefox" then
    tell application "System Events"
        keystroke return
    end tell
end if

2018-09-14 [cocoa][swift]Web API通信の符号化について(ASN.1, JSON, MessagePack, ProtocolBuffers, FlatBuffers, Avro)

本発表では、Web API 通信の基本と符号化について説明する。

HTTP通信
Web API通信とは、Webブラウザでホームページを閲覧する際のHTTP通信と同じ物だ。サーバから得られた結果を文書としてWebブラウザで表示するか、プログラムで利用する為のデータを取得するかの違いでしかない。
Webブラウザのアドレス欄に、「http://www.bitz.co.jp/index.html」と入力すると、Webブラウザはサーバに対して以下の要求を送る。

GET /index.html HTTP/1.1
Host: www.bitz.co.jp
Accept: image/pict, image/postscript, */*
Accept-Language: en
Connection: Keep-Alive
Uset-Agent: ave-front/1.0

すると、サーバは、以下の応答を返す。

HTTP/1.1 200 OK
Date: Sat, 25 Aug 2018 23:40:00 GMT
Server: Apache/1.3.9 (Unix)
Last-Modified: Fri, 24 Aug 2018 09:30:00 GMT
ETag: “12345-123-12345678”
Content-Length: 141
Content-Type: text/html
 
>html<
	>head<>title<INDEX>/title<>/head<
	>body<
		本文
	>/body<
>/html<

途中の空行より上がヘッダー部、下がボディ部となる。
Webブラウザは、応答のボディ部のデータを表示している。
Web API通信では、ボディ部に符号化されたデータが格納されることになる。

ASN.1
通信のデータ構造、エンコード、デコードを記述する記法で、たとえば、SNMPで利用されている。RSA公開鍵を生成する必要があって、ASN.1の知識が必要になったことがあったが、その際、調べた事を説明する。
RSA公開鍵は、modulusとpublicExponentの二つのパラメータで構成されている。他のプラットフォームでは、この二つのパラメータからRSA公開鍵を生成するAPIが用意されていたのだが、iOSでは用意されていなかったので、独自に対応した。
RSA公開鍵のASN.1での定義は以下のとおり。

RSAPublicKey ::= SEQUENCE {
    modulus INTEGER, -- n
    publicExponent INTEGER -- e
}

ASN.1のエンコード方式は複数あるのだが、RSA公開鍵はDERということなので、これについて説明する。
SEQUENCEやINTEGER等のオブジェクトの形式は以下のとおり。

タグ 長さ

タグの番号は以下のとおり。

タグ タグ番号 説明
INTEGER 0x02 整数型
SEQUENCE 0x10 構造体 / 配列

オブジェクトのタグ欄には、タグ番号がそのまま格納されるのではなくて以下の形式となっている。

8 7 6 5 4 3 2 1
クラス
00: 汎用
構造化フラグ
タグ番号

7〜8bitのクラスは、汎用型のSEQUENCEとINTEGERのみなので、RSA公開鍵では00となる。
6bitの構造化は、INTEGERは単一型(0)、SEQUENCEは構造型(1)となる。なので、タグ欄にはINTEGERにはタグ番号そのものが設定され、SEQUENCEは、00+1+10000=0x30となる。
長さ欄は、以下のとおり。

8 7 6 5 4 3 2 1
0 長さ(127オクテット以下)

8bitに収まらない場合は以下となる。

8 7 6 5 4 3 2 1
1 長さ部の長さn
8 7 6 5 4 3 2 1
トークン1

・・・

8 7 6 5 4 3 2 1
トークンn

JSON
JSONは、JavaScriptのオブジェクトの表記法をベースとした軽量なデータ記述書式で、人にとっての読み書きしやすいのが特徴だ。

{
    "name" : "Pear",
    "points" : 250,
    "description" : "A ripe pear."
}

CocoaでJSONを扱うために以前からJSONSerializationというクラスが用意されていたが、その後追加されたJSONEncoderとJSONDecoderを利用すれば、JSONの扱いもより一層簡単になる。
以下は、構造体からJSONデータを生成するサンプルだ。

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
}
 
let pear = GroceryProduct(name: "Pear", points: 250, description: "A ripe pear.")
 
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
 
let data = try encoder.encode(pear)
print(String(data: data, encoding: .utf8)!)

以下は、逆にJSONデータから構造体のインスタンスを得るサンプルだ。

let json = """
{
    "name": "Durian",
    "points": 600,
    "description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
 
let decoder = JSONDecoder()
let product = try decoder.decode(GroceryProduct.self, from: json)
 
print(product.name) // Prints "Durian"
print(product)

MessagePack
MessagePackはJSON感覚で利用できる符号化の仕組みで、公開されて仕様をもとに本家以外からも各種環境のライブラリが提供されている。どの符号化方式を選ぶから、クライアント側の都合のみではダメで、サーバ側の環境向けのライブラリが用意されているかが重要だ。この条件に合致するMessagePackはJSONの次に採用される符号化の資格が十分あるが、我々Cocoaプログラマにとっての不満は、Objective-C / Swift のライブラリが充実していないということだ。
なので、軽く触れる説明するにとどめる。

{ “compact”:true, “schema@:0 }

これをMessagePackで符号化すると以下のとおり。

[82][A7]compact [C3][A6]schema [00]
[82]
2-element map
[A7]compact
7-byte string
[C3]
true
[A6]schema
6-byte string
[00]
integer 0

バイナリ書式なので、27bytesから18bytesに短縮。

ProtocolBuffers
インタフェース定義言語(IDL)で構造を定義する符号化方式で、大規模なプロジェクトの場合、仕様書からIDLを出力し、IDLで定義した内容でクライアントとサーバが通信するという利点がある。
Googleが開発したものだが、本家からObjective-C / Swiftのライブラリが提供されていないという欠点があったのだが、なんと、Appleから提供されるようになった!

例。定義ファイル (.proto ファイル) を用意する。

syntax = "proto3";
 
message BookInfo {
   int64 id = 1;
   string title = 2;
   string author = 3;
}

Swiftコードを生成する。

$ protoc --swift_out=. DataModel.proto

これを利用してエンコード / デコードする。

// Create a BookInfo object and populate it:
var info = BookInfo()
info.id = 1734
info.title = "Really Interesting Book"
info.author = "Jane Smith"
 
// As above, but generating a read-only value:
let info2 = BookInfo.with {
    $0.id = 1735
    $0.title = "Even More Interesting"
    $0.author = "Jane Q. Smith"
  }
 
// Serialize to binary protobuf format:
let binaryData: Data = try info.serializedData()
 
// Deserialize a received Data object from `binaryData`
let decodedInfo = try BookInfo(serializedData: binaryData)
 
// Serialize to JSON format as a Data object
let jsonData: Data = try info.jsonUTF8Data()
 
// Deserialize from JSON format from `jsonData`
let receivedFromJSON = try BookInfo(jsonUTF8Data: jsonData)

FlatBuffers
これもGoogle製の符号化方式で高パフォーマンスが特徴だ。
ただ、本家からObjective-C / Swiftライブラリが提供されていないので紹介するにとどめる。

Apache Avro
これも本家からObjective-C / Swiftライブラリが提供されていないということで、簡単な紹介にとどめる。
ProtocolBuffersと比較される符号化方式となるが、特徴なのはC#がサポートされていて、Unityで利用できるということ。これで採用されているプロジェクトがあるようだ。


2018-09-13 [cocoa][swift]Cocoa.swift 2018-10のご案内

次回勉強会の案内です。

開会から名称が、Cocoa.swift (Cocoa勉強会 関東 Swift分科会) と変わります。今後、池袋以外での開催を考えているのと、Cocoaといえば、この勉強会と認知されているので、それを名称にしました!


■ 第115回 Cocoa勉強会 関東 のご案内
日時:
2018/10/17(水) 19:30-22:00
会場:
池袋コワーキングスペース OpenOffice FOREST
http://co-forest.com
集合:
現地
座席代:
夜間ドロップイン 一人1,000円

■出欠の登録

以下のconnpassサイトからお願いします。
https://cocoa-kanto.connpass.com/event/101034/


[スケジュール]
18:00  自習
19:30  自己紹介
20:00  発表
- 20:00〜20:15 「募集中」未定
- 20:15〜20:30 「募集中」未定
- 20:30〜20:45 「募集中」未定
- 20:45〜21:00 「募集中」未定
- 21:00〜21:15 「募集中」未定
- 21:15〜21:30 「募集中」未定
- 21:30〜21:45 「募集中」未定
21:45  次回日程の決定、片づけ
22:00  解散

2018-08-12 [cocoa][swift]Cocoa.swift 2018-09のご案内

次回勉強会の案内です。

秋に特別な勉強会の開催を検討しています。各自取り組んでいることをしっかりと発表できる場になればと考えています。場所も何時もの池袋以外を考えているのですが、このタイミングで勉強会の名称の修正を考えています。

Cocoa勉強会 関東 Swift分科会ということで、Cocoa.swiftでどうでしょうか?

今後も池袋以外での開催の可能性があるとうことで名称に場所を含めない。そして、Cocoa勉強会の分科会ということでBUKUROをCocoaに差し替える。ご意見がある方は、九月の勉強会でお聞かせください。


■ 第114回 Cocoa勉強会 関東 のご案内
日時:
2018/09/12(水) 19:30-22:00
会場:
池袋コワーキングスペース OpenOffice FOREST
http://co-forest.com
集合:
現地
座席代:
夜間ドロップイン 一人1,000円

■出欠の登録

以下のconnpassサイトからお願いします。
https://cocoa-kanto.connpass.com/event/97193/


[スケジュール]
18:00  自習
19:30  自己紹介
20:00  発表
- 20:00〜20:30 「募集中」未定
- 20:30〜21:00 「符号化について」村上幸雄
  ASN.1, JSON, MessagePack, ProtocolBuffers, FlatBuffers, Avro
- 21:00〜21:15 「募集中」未定
21:15 モブプログラミング
- Master-Detail App を実装する(モデルをどう持つ?etc)
21:30 秋の大勉強会について考える
21:45  次回日程の決定、片づけ
22:00  解散

2018-07-25 [cocoa][swift]BUKURO.swift 2018-07に行ってきた

会場はいつもの池袋コワーキングスペース Open Office FOREST。前回の勉強会は新生MOSA始動のイベントだったので、通常の勉強会は久しぶりだ。

_ VirtualBoxへのMojaveのインストール

VirtualBoxへmacOS Mojaveをインストールするという発表だが、MojaveはUSB1.1コントローラがサポートされていないのと、インストール時にフォーマットをAPFSに変更してしまうのだが、VirtualBoxがAPFSからの起動をサポートしていないという問題があり、それを回避する内容だ。

_ 通信デバッグ

Remote Virtual Interfaceを使って、Macに接続されたiOS端末をMacのターミナルで動かしたtcpdumpでパケット・トレースするという内容だ。

_ SwiftのOptionalを理解する

Swiftでプログラミングする範囲で必要なoptional関連の構文とサンプルコードをスライドにして、それを参加者と内容を確認していくといった内容だ。

_ 構文糖衣なしでSwiftのオプショナルを使うとどうなるか?

前の発表を踏まえて、Optionalの内部での定義を紹介して、より深く理解するという内容だ。

予定していた『モブプログラミング』は時間切れとなり次回以降の実施となった。

秋に田町のコワーキングスペース 森永ヴィレッジで、特別な勉強会の開催が発表されたのだが、現状、勉強会の名称が統一されていないのと、池袋以外での開催となるので、新たな名称として『Cocoa.swift』の提案があった。

より詳細な内容については、FacebookのCocoa勉強会 関東のグループで行うことになったので、是非、意見をいただければ。


2018-07-13 [モブプログラミング]Master-Detail App を実装する

勉強会初の試みとして、モブプログラミングに挑戦します。事前に用意した内容を公開します。

進め方

  • 大まかな仕様があるので、それを参考にして皆で製作する。
    • スムーズにするため、サンプル・プロジェクトを用意する。
    • プロジェクトは共有した方がいいが大変なので、各自ローカルの環境でとする。
  • 役割は、一人がドライバ、その他がナビゲータ。
  • ドライバーは時間を決めて、順番に交代して担当する。時間は例えば5分。
  • ドライバーは操作しているMacの画面を投影して、皆で見ながら進める。
  • 大まかな仕様とサンプルと異なる実装方法の意見があると思うが、それを皆で共有して議論したいので、異なる意見は大いに歓迎。

作るもの

  • iOSのMaster-Detail App。
    • ほぼ、新規プロジェクトで生成される雛形のまま。
  • モデルのクラスを用意する。
    • サンプルのモデルのクラス名は、Document。
    • シングルトンでなく、AppDelegateのメンバー変数で保持。
Example

_ ソースコード

たたき台となるExampleを共有ディスクで公開しています。
Exampleプロジェクト

2018-07-08 [Swift]SwiftのOptionalを理解する

  • Lispでは、無を表すものとしてnilを用意。
  • C言語では、空ポインタとしてNULLマクロを定義。
  • Objective-Cでは、空idとしてnilが用意され、画期的なのはnilにメッセージを送信しても無視されるだけでエラーにならない!
  • SwiftのnilはObjective-Cとの互換。Cocoaフレームワークを利用するためか。
var a : Int = 1
var b : Int? = 2
a = nil  // エラー
b = nil  // OK
b = Int(“abcd”)  // nil
var c : Optional = 3  // パラメータ付き型指定

開示(unwrap)

var a : Int? = 1234
var b : Int = a - 2  // 型が異なるのでコンパイル・エラー
var b : Int = a! - 2  // 開示指定する
a = nil
b = a! - 2  // 実行時エラー
 
a = 5678
if a != nil {
    print(“\(a!)”)  // 開示指定が必要
}
print(String(describing: a))  // Debug目的で

オプショナル束縛構文 optional binding

var num : Int? = 1234
if let n = num {
    print(“\(n)”)
}
 
if var n = Int(“1234”) {
    n += 5678
    print(“\(n)”)
}
 
if let n = Int(“1234”), let m = Int(“5678”) {
    print(“\(n + m)”)
}
 
var a : Int? = 1
while let n = a {
    a = nil
}

guard文

guard 条件 else { /* breakやreturn */ }
 
func demo(_ num:Int?) {
    guard let n = num else { return }
    print(“\(n)”)  // 変数nが使える
}

nil合体演算子

let n : Int? = 1234
 
let m = (n != nil) ? n! : 0
let m = n ?? 0
 
let a : Int? = nil
let b : Int? = nil
let c : Int? = 3
let = a ?? b ?? c ?? 0  // cの値
func demo(_ p: inout Int?) {
    p = nil
}
var n: Int? = 1234
demo(&n)
print(n ?? “nil”)
 
func test(_ num: inout Int) {
    num = 0
}
n = 5678
test(&n!)  // nがnilだと実行時エラー
print(n ?? “nil”)

有値オプショナル型 (IUO)

let n : Int! = 1234
print(“\(n)”)  // 開示指定は不要
 
var m : Int! = nil
m += 5678  // 実行時エラー
print(“\(m)”)

失敗のあるイニシャライザ

struct Demo {
    var a = 0
    init?(_ n:Int) {
        if n < 0 {
            return nil
        }
        a = n
    }
    init() {
        a = 1234
    }
}
var p: Demo = Demo()
var q: Demo? = Demo(5678)

キャスト演算子

  • 式 is T

    型/プロトコルTなら真
  • 式 as T

    型/プロトコルTにキャスト
  • 式 as? T
    型/プロトコルTのオプショナルにキャスト
    失敗した場合はnil
  • 式 as! T
    型/プロトコルTにキャスト
    失敗した場合は実行時エラー

オプショナルチェーン optional chaining

// 辿っている途中でnilがあれば、
// そこで止まり全体でnilとなる。
if let name = who?.club?.teacher?.name {
    print(name)
}

トップ 追記