- 他のファイルのコンパイルエラーが影響してしまう場合がある。その時は再起動。
swift package generate-xcodeproj
でXcodeのプロジェクトファイルを生成する。
フォルダ構成とPackage.swiftにしたがって、必要に応じて�プロジェクトファイルとか作る感じ SwiftPMはクロスプラットフォームだけど、プロジェクトファイルはMacのXcode用なので
- REPLモードを終了するにはコマンド「:quit」もしくは「:q」を入力する。
- 暗黙的型変換はあまり無い
- 明示的な型変換 C#でいうこれが無い
- ExpressibleByStringLiteral
- "型の安全性を重視したSwiftらしい仕様です。”
let v = Int64.init(a)
省略形 ->let v = Int64(a)
- コード補完が効くのであえて後者で記述した後、前者の書き方に直したりする。
CustomStringConvertible
プロトコルは、C# でいう.ToString()
したいときに実装する。CustomStringConvertible
プロトコルはdescription
メソッドを持つ。なので、プロトコルに準拠しない場合description
メソッドは避けるのが一般的っぽい。
- ところどころでスペースがある。
- 例:
let array: [Int] = []
変数名の後:
- SwiftLintいれる
- 例:
- Dictionary<Key,Value>
= nil
で要素削除ができる- 更新でキーがなかったら追加できる
- 参考:値がOptionalなDictionaryのnil値の取り扱い – Swift・iOSコラム – Medium
1..<4
: 1, 2, 31...4
: 1, 2, 3, 4let a = "a"..."z"
ClosedRange- nil を文字列で表したとき
nil
と出力される?→null不許可で防げるのでそういうケースは無いはず。 ImplicitlyUnwrappedOptional<Wrapped>
は、Int!と表記できます。”- 値の取り出し方が違う。
tuple 代入によるアクセス:
let int: Int
let string: String
(int, string) = (1, "a")
int // 1
string // "a"
var a = 1
var b = 2
(b, a) = (a, b)
タプルでのアップキャスト:
class Base {
}
class D1 : Base {
}
class D2 : Base {
}
struct Pair<T1, T2>
{
let item1: T1
let item2: T2
}
//普通の変数
let a1: Base = D1()
//Tuple 1
let t1:(D1, D2) = (D1(), D2())
print(t1)
//Tuple 2 エラー
let t0:(Base, Base) = (D1(), D2())
//let t2:(Base, Base) = t1
//let t3:(Base, Base) = t1 as (Base, Base)
//↓これならOK
let t4:(Base, Base) = (t1.0, t1.1)
//Generics
let x1: Pair<Int, Int> = Pair(item1: 1, item2: 2)
let x2: Pair<D1, D2> = Pair(item1: D1(), item2: D2())
//エラー(C#とかでも無理)
//let x3: Pair<Base, Base> = x2
//let x3: Pair<Base, Base> = x2 as Pair<Base, Base>
下の2つは糖衣構文:
if 1 == 1 && 2 == 2 && 3 == 3 {
}
if 1 == 1, 2 == 2, 3 == 3 {
}
if case:
“if case パターン = 制御式 {
制御式がパターンにマッチした場合に実行される文
}
case .some(let a) where a > 10:
for case:
“for case パターン in 値の連続 {
要素がパターンにマッチした場合に実行される文
}
- defer文
- スコープ退出後に実行。コンパイル後どうなってる?
func hoge() {
defer { print("1") }
defer { print("2") }
return
defer { print("3") }
defer { print("4") }
}
hoge()
出力:
2
1
~=
演算子- p.183 Enum 連想値のパターンマッチ
- 「連想値」は付属情報、みたいな意味。
- enum の連想値、protocol の連想値。など。
関数の引数、外部引数名と内部引数名:
func invite(user: String, to group: String) {
//to: 外部引数名
//group: 内部引数名
print("\(user) is invited to \(group).")
}
invite(user: "Ishikawa", to: "Soccer Club")
引数名を参照時に使うかどうか:
func sum(_ int1: Int, _ int2: Int) -> Int {
return int1 + int2
}
let result = sum(1, 2) // 3
//let result = sum(int1: 1, int2: 4) //コンパイルエラー
↑外部引数名(外部ラベル)を省略している場合、呼び出すときは、引数名を使えない。 メソッドの定義者が明確に使い方を指定する。どっちでもいいというのはできない。
inout
引数。呼び出すときは&
をつける。&
は参照渡しの一般的な記号。- obj-Cの時は
*hoge
だった? なぜ、Swift では、inout
と&
で分けているのか?→不明。
func hoge() -> Void { }
、Void
はつけてもつけなくても良い。
↓ 文が一つのとき、return は省略できる
let double = { (x: Int) -> Int in
x * 2
}
- クロージャ:型とか引数とか、決まっているのならいろいろ省略できる
- 簡略引数名
do { }
のdo
は省略できない。(C# は{}
だけでスコープ分けられる)
- 簡略引数名
- キャプチャという概念
@escaping
- “escaping属性は、関数に引数として渡されたクロージャが、関数のスコープ外で保持される可能性があることを示す属性です。
@autoclosure
@autoclosure
を宣言することで、普通の式をクロージャとしてみなすことができる→遅延評価に使われる
- トレイリングクロージャ:最後の引数のクロージャを、()の外にかけるやつ。
- ストアドプロパティ
- willset
- newValue
- didSet
- willset
- コンピューテッドプロパティ:get, set
newValue
は任意の名前に変えられる:
var fahrenheit: Double {
get {
return (9.0 / 5.0) * celsius + 32.0
}
set(newFahrenheit) {
celsius = (5.0 / 9.0) * (newFahrenheit - 32.0)
}
}
- 失敗可能イニシャライザ(failable initializer)
Int?(...)
- p.143 いい感じ
- 戻り値のオーバーロードが可能
get {}
省略可能- subscript
subscript(index: Int) -> Int { ...
サブスクリプトではすべての外部引数名がデフォルトでは
_
となっている点が 異なります。
_
が隠れて宣言されている感じ?- 外部ラベル
_
をつけてみる→動く - 他の外部ラベルをつけてみる→動かない
- 外部ラベル
convenience initializer
- イニシャライザの中で自身の別のイニシャライザを呼ぶ必要がある
- 型のネスト、NewsFeedItem.Kind型
- 【Swift】レイジ―プロパティ(lazy)の使い方。最初にアクセスされたときに初期値が決まる。 | はじはじアプリ体験記
- 言語の仕様と、ライブラリで実装する違いは?
- Swiftの変数&定数が、自動的にlazyとして扱われる条件を調べた時のメモ - 開発メモ
- グローバル変数がlazyになることについて:The Swift Programming Language (Swift 4): Properties "Global and Local Variables"
mutating
自身を変えようとしている。(ミュータントみたいな)
extension Int {
mutating func increment() {
self += 1
}
}
var a = 1 // 1
a.increment() // 2(aに再代入が行われている)
let b = 1
b.increment() // bに再代入できないためコンパイルエラー
- 定数のストアドプロパティは変更できない
- メンバーワイズイニシャライザ(イニシャライザの定義を省略できる)
final
サブクラスでのオーバーライド禁止- classやメソッドで使用可能
- クラスプロパティ・クラスメソッド:オーバーロード可能
- static : オーバーロードできない
ルール:
- 指定イニシャライザは、スーパークラスの指定イニシャライザを呼ぶ
- コンビニエンスイニシャライザは、同一クラスのイニシャライザを呼ぶ
- コンビニエンスイニシャライザは、最終的に指定イニシャライザを呼ぶ
ルール2:
- クラス内で新たに定義されたすべてのストアドプロパティを初期化し、スーパー クラスの指定イニシャライザを実行する。
- スーパークラスでも同様の初期化を行い、大もとのクラスまでさかのぼる
- ストアドプロパティ以外の初期化を行う
他
- デフォルトイニシャライザ
- プロパティが存在しない場合や、すべてのプロパティが初期値を持っている場合、記述を省略できる。
- デイニシャライザ
deinit
==
Equtable===
インスタンス参照
-
イニシャライズ、コンピューテッドプロパティかける
-
rawValue
-
associated value
(連想値)(付加情報) -
C# と完備性について:小ネタ チェック例外とUnion型 | ++C++; // 未確認飛行 C ブログ
- 継承するクラス名→プロトコルの準拠、という順番
- 可読性を高くするため、extension : protocol = 1:1 が一般的?
- プロトコルのプロパティにはストアドプロパティやコンピューテッドプロパティといった区別がない
-
let
は使用できない typealias
便利そう- AssociatedType(連想型って呼んでる?)
typealias AssociatedType = Int
- 実装から省略できる
AssociatedType
と同じ型をネスト型で定義する
static 宣言できる:
protocol Person {
static func memo()
}
class Taro: Person {
static func memo() {}
}
http://swift.sandbox.bluemix.net/#/repl/59982e74add9952abb4026f3
- http://swift.sandbox.bluemix.net/#/repl/59982c81add9952abb4026f2
- protocol で宣言しない場合、メソッドの上書きが行われず、型に対しての拡張というイメージ
- protocol extension は、interface & implement じゃなく、ただの implementという感じ。
protocol Item {
var name: String { get }
//var description: String { get } //←⭐️このプロパティをコメントアウトするかどうかで出力が違う
}
extension Item {
var description: String {
return "商品名: \(name)"
}
}
struct Book : Item {
let name: String
var description: String {
return "しょうひんめい2: \(name)"
}
}
let book:Item = Book(name: "Swift実践入門") //変数はItem型として定義
print(book.description)
C#の場合:
using System;
// C# の場合は object.ToString があるんだけど、挙動の説明のために別のを用意
interface Item
{
string Name { get; }
//string ToStr(); //←⭐️このメソッドをコメントアウトするかどうかで出力が違う
}
static class ItemExtensions
{
public static string ToStr(this Item i) => "extention: " + i.Name;
}
struct Book : Item
{
public string Name { get; }
public Book(string name) : this() => Name = name;
public string ToStr() => "struct: " + Name;
}
class Program
{
static void Main()
{
Item book = new Book("C# 入門");
Console.WriteLine(book.ToStr());
}
}
WebContentにも準拠している場合に有効:
extension Item where Self : WebContent {
“struct IntSequence : Sequence, IteratorProtocol {}
だと、next()だけの実装でよい。理由は下のような実装があるから:
extension Sequence where Self.Iterator == Self {
/// Returns an iterator over the elements of this sequence.
public func makeIterator() -> Self
}
受け取り変数の方による推論が可能。
func someFunction<T>(_ any: Any) -> T? { return any as? T }
連想型の制約
func sorted<T : Collection>(_ argument: T) -> [T.Iterator.Element]
where T.Iterator.Element : Comparable {
return argument.sorted()
}
sorted([3, 2, 1]) // [1, 2, 3]
- open
- モジュール内外のすべてのアクセスを許可する
- public
- モジュール内外のすべてのアクセスを許可するが、モジュール外で継承したりオーバーライドすることはできない
- internal
- 同一モジュール内のアクセスのみを許可する
- fileprivate
- 同一ソースファイル内のアクセスのみを許可する
- private
- 対象の要素が属しているスコープ内のアクセスのみを許可する
fileprivateについて: 4でextension間でprivate参照できるようになって、fileprivate出番自体がほぼなくなった。仕様的にはあるけどほぼ不使用で良い系。
“型全体のデフォルトのアクセスレベルは、internalです。”
コメント:
通常のコメント
// コメント
/*
* コメント
*/
ドキュメントコメント
/// ドキュメントコメント
/**
* ドキュメントコメント
*/
ImplicitlyUnwrappedOptional型を使う例:
class SuperClass {
var one = 1
}
class BaseClass : SuperClass {
var two: Int!
override init() {
super.init()
two = one + 1
}
}
- Swift では、C# でいう「interfaceの明示的な実装」がない。Swiftでは、メソッド名・引数名を以下のようにして被らないようにするのが一般的。(例:メソッド名・引数にも主語)
public protocol UITableViewDelegate : NSObjectProtocol,
UIScrollViewDelegate {
optional public func tableView(
_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
“こうした命名規則によって、このメソッドを誰が、いつ、どういう場合に呼ぶのかが明確になります。tableView(_:didSelectRowAt:)メソッドの例では、セルが選択された(selectRow)あと(did)にUITableViewクラスが呼ぶということが、メソッド名を見ただけでわかります。”
- 強参照(デフォルト)
- 参照カウントを1つカウントアップする
- 弱参照
- カウントアップしない
- 実行時に参照先のインスタンスがすでに解放されている可能性があることを意味
“通常は、デリゲート元からデリゲート先への参照を弱参照として、循環参照を回避します”
キャプチャリスト
- weak
- Optionalとして再定義
- unowned
- 実行時エラー
import PlaygroundSupport
import Dispatch
// Playgroundでの非同期実行を待つオプション
PlaygroundPage.current.needsIndefiniteExecution = true
class SomeClass {
let id: Int
init(id: Int) {
self.id = id
}
}
do {
let object = SomeClass(id: 42)
let closure = { [weak object] () -> Void in
if let o = object {
print("objectはまだ解放されていません: id => \(o.id)")
} else {
print("objectはすでに解放されました")
}
}
print("ローカルスコープ内で実行: ", terminator: "")
closure()
let queue = DispatchQueue.main
queue.asyncAfter(deadline: .now() + 1) {
print("ローカルスコープ外で実行: ", terminator: "")
closure()
}
}
@escaping
“escaping属性は、関数に引数として渡されたクロージャが、関数のスコープ外で保持される可能性があることを示す属性です。
クロージャの内部は、インスタンス自身にアクセスするときself
キーワードをつける必要がある。循環参照に気づきやすくするため。
typealias
による型エイリアス:
�typealias CompletionHandler = (Int?, Error?, Array<String>?) -> Void
- GCD :
DispatchQueue.main
- Operation, OperationQueue
- Threadクラス
- 失敗可能イニシャライザとかで使う
- 標準ライブラリで定義されていない。
- 代表的な実装:antitypical/Result: Swift type modelling the success/failure of arbitrary operations.
“Swiftのコミュニティではデファクトスタンダードとなっており、Swiftにおけるエラー処理を説明するうえでは無視できない概念です。”
↑デファクトスタンダードだとちょっと強いかもしれない、一般的なパターンな感じっぽい。
なぜ、C#ではResult<T, Error>形式が一般的ではないか?
- 独自実装で使用していることも多くあるが、パターンというほどでもない可能性
- Associated Value enum が使えないため、エラーと値の組み合わせが担保できない(実装が複雑になる)→パターンにならない
- C# 7でValue Tuppleが最近でた
- Associated Value Enum がない (これが一番の理由っぽそう)
do {} catch {}
thows
- “関数、イニシャライザ、クロージャの定義にthrowsキーワードを追加すると、それらの処理の中でdo-catch文を用いずにthrow文によるエラーを発生させることができます。”
func 関数名(引数) throws -> 戻り値の型 {
throw文によるエラーが発生する可能性のある処理
}
rethrows
- 引数クロージャのエラー伝搬
- 引数クロージャのエラー以外のエラーは発生できない
try
throws
が指定された関数を呼び出すためのキーワード- do節・throws付きの関数内でのみ使用可能
try!
- エラー処理を記述しない場合とかに使う
- do節・throws付きの関数以外でも使用可能
try?
- do節・throws付きの関数以外でも使用可能
- その代わり関数の戻り値がOptional
失敗可能イニシャライザ(failable initializer)は、throwsにするかinit?にするか:
- 例外情報を知りたい:throws
- init? だと、nilかそうでないかを区別するだけでよい(throwsだと例外のことについて考える必要がある)
- どっちか迷ったらthrowsがよさげ?
- throwsだと、使う側が選択の余地があり、一手間でOptionalとして扱える
Swift.Error
: Errorが標準ライブラリのものを示す時に、Swift.
をつけて使う(自分で定義したものと区別するため)
defer
併用:
enum Error : Swift.Error {
case someError
}
func someFunction() throws {
print("Do something")
throw Error.someError
}
func cleanup() {
print("Cleanup")
}
do {
defer {
// do節を抜けたタイミングで必ず実行される
cleanup()
}
try someFunction()
} catch {
print("Error: \(error)")
}
出力:
Do something
Cleanup
Error: someError
↑C#のtry-catch-finalyだと、Cleanup
とError: someError
が逆になる。(catchとtry内の二重記述になるかもしれない)
Result<T, Error>
は、Error引数の型しか扱えない。do-catchは、Errorプロトコルに準拠していればOKResult<Void, Error>
だと、エラーを無視していることがコード上から分かりにくい。
“本節で説明するfatalError(_:)関数は、その箇所が実行されること自体が想定外であることを宣言するための関数です。”
- Never型
- fatalError(_:)関数の戻り値
func randomInt() -> Int {
fatalError("まだ実装されていません")
}
assert(_:_:)
assertFailure(_:)
Swiftのエラー4分類が素晴らしすぎるのでみんなに知ってほしい - Qiita SwiftはどのようにJavaの検査例外を改善したか - Qiita