感想ノート:Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus) | 石川 洋資, 西山 勇世 | Amazon https://www.amazon.co.jp/dp/4774187305/?tag=aokj24gaw-22
swift package generate-xcodeproj
でXcodeのプロジェクトファイルを生成する。
フォルダ構成とPackage.swiftにしたがって、必要に応じてプロジェクトファイルとか作る感じ SwiftPMはクロスプラットフォームだけど、プロジェクトファイルはMacのXcode用なので
let v = Int64.init(a)
省略形 -> let v = Int64(a)
CustomStringConvertible
プロトコルは、C# でいう.ToString()
したいときに実装する。
CustomStringConvertible
プロトコルはdescription
メソッドを持つ。なので、プロトコルに準拠しない場合description
メソッドは避けるのが一般的っぽい。let array: [Int] = []
変数名の後:
= nil
で要素削除ができる1..<4
: 1, 2, 31...4
: 1, 2, 3, 4let a = "a"..."z"
ClosedRangenil
と出力される?→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 値の連続 {
要素がパターンにマッチした場合に実行される文
}
func hoge() {
defer { print("1") }
defer { print("2") }
return
defer { print("3") }
defer { print("4") }
}
hoge()
出力:
2
1
~=
演算子関数の引数、外部引数名と内部引数名:
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
引数。呼び出すときは&
をつける。
&
は参照渡しの一般的な記号。*hoge
だった? なぜ、Swift では、inout
と&
で分けているのか?→不明。func hoge() -> Void { }
、Void
はつけてもつけなくても良い。↓ 文が一つのとき、return は省略できる
let double = { (x: Int) -> Int in
x * 2
}
do { }
の do
は省略できない。(C# は{}
だけでスコープ分けられる)@escaping
@autoclosure
@autoclosure
を宣言することで、普通の式をクロージャとしてみなすことができる→遅延評価に使われるnewValue
は任意の名前に変えられる:
var fahrenheit: Double {
get {
return (9.0 / 5.0) * celsius + 32.0
}
set(newFahrenheit) {
celsius = (5.0 / 9.0) * (newFahrenheit - 32.0)
}
}
Int?(...)
get {}
省略可能subscript(index: Int) -> Int { ...
サブスクリプトではすべての外部引数名がデフォルトでは
_
となっている点が 異なります。
_
が隠れて宣言されている感じ?
_
をつけてみる→動くconvenience initializer
mutating
自身を変えようとしている。(ミュータントみたいな)
extension Int {
mutating func increment() {
self += 1
}
}
var a = 1 // 1
a.increment() // 2(aに再代入が行われている)
let b = 1
b.increment() // bに再代入できないためコンパイルエラー
final
サブクラスでのオーバーライド禁止
ルール:
ルール2:
他
deinit
==
Equtable===
インスタンス参照イニシャライズ、コンピューテッドプロパティかける
rawValue
associated value
(連想値)(付加情報)
C# と完備性について:小ネタ チェック例外とUnion型 | ++C++; // 未確認飛行 C ブログ
let
は使用できないtypealias
便利そうtypealias AssociatedType = Int
AssociatedType
と同じ型をネスト型で定義するstatic 宣言できる:
protocol Person {
static func memo()
}
class Taro: Person {
static func memo() {}
}
http://swift.sandbox.bluemix.net/#/repl/59982e74add9952abb4026f3
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]
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
}
}
public protocol UITableViewDelegate : NSObjectProtocol,
UIScrollViewDelegate {
optional public func tableView(
_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
“こうした命名規則によって、このメソッドを誰が、いつ、どういう場合に呼ぶのかが明確になります。tableView(_:didSelectRowAt:)メソッドの例では、セルが選択された(selectRow)あと(did)にUITableViewクラスが呼ぶということが、メソッド名を見ただけでわかります。”
“通常は、デリゲート元からデリゲート先への参照を弱参照として、循環参照を回避します”
キャプチャリスト
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
DispatchQueue.main
“Swiftのコミュニティではデファクトスタンダードとなっており、Swiftにおけるエラー処理を説明するうえでは無視できない概念です。”
↑デファクトスタンダードだとちょっと強いかもしれない、一般的なパターンな感じっぽい。
なぜ、C#ではResult<T, Error>形式が一般的ではないか?
do {} catch {}
thows
func 関数名(引数) throws -> 戻り値の型 {
throw文によるエラーが発生する可能性のある処理
}
rethrows
try
throws
が指定された関数を呼び出すためのキーワードtry!
try?
失敗可能イニシャライザ(failable initializer)は、throwsにするかinit?にするか:
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(_:)関数は、その箇所が実行されること自体が想定外であることを宣言するための関数です。”
func randomInt() -> Int {
fatalError("まだ実装されていません")
}
assert(_:_:)
assertFailure(_:)
Swiftのエラー4分類が素晴らしすぎるのでみんなに知ってほしい - Qiita SwiftはどのようにJavaの検査例外を改善したか - Qiita