The Swift Programming Languageの気になった所まとめ

  • 15
    いいね
  • 8
    コメント

今年は、会社で半年ほどかけて The Swift Programming Language の読書会を開催していました。
気になった点をSlackで共有しながら読み進めるというスタイルで実施していて、細かい所(特にNOTEの部分)に学びが多かったです。
わすれないうちに、Slackの内容をここにまとめておきます。(Language Guideのみ)

Strings and Characters

String and Character Equality

Extended grapheme clusters are canonically equivalent if they have the same linguistic meaning and appearance, even if they are composed from different Unicode scalars behind the scenes.

書記素クラスタは異なるUnicode scalarsで構成されていても同じ言語上の意味と外観を持つなら等価とするんですね。

Comparing Strings

Conversely, LATIN CAPITAL LETTER A (U+0041, or "A"), as used in English, is not equivalent to CYRILLIC CAPITAL LETTER A (U+0410, or "А"), as used in Russian. The characters are visually similar, but do not have the same linguistic meaning:

    let latinCapitalLetterA: Character = "\u{41}"

    let cyrillicCapitalLetterA: Character = "\u{0410}"

    if latinCapitalLetterA != cyrillicCapitalLetterA {
        print("These two characters are not equivalent.")
    }
    // Prints "These two characters are not equivalent."

"A" != "А"。よく見ると違いますね。

Unicode Representations of Strings

You can iterate over the string with a for-in statement, to access its individual Character values as Unicode extended grapheme clusters.

for-inで見た目どおりの1文字ずつにアクセスできます。

Collection Types

⁠⁠⁠⁠Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations. ⁠⁠⁠⁠

型最高!ものすごくわかりやすい言葉で説明されていますね。

Mutability of Collections

It is good practice to create immutable collections in all cases where the collection does not need to change. Doing so enables the Swift compiler to optimize the performance of the collections you create.

immutableにすると、パフォーマンスが上がるらしいです。

Creating an Array by Adding Two Arrays Together

You can create a new array by adding together two existing arrays with compatible types with the addition operator (+). The new array’s type is inferred from the type of the two arrays you add together:

    var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
    // anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]   
    var sixDoubles = threeDoubles + anotherThreeDoubles
    // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

let arr = ["a", "b", "c"] + [1 ,2 ,3]とするとtype(of: arr)Array<Any>
になりました。なるほど。

Accessing and Modifying an Array

If you try to access or modify a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error. You can check that an index is valid before using it by comparing it to the array’s count property. Except when count is 0 (meaning the array is empty), the largest valid index in an array will always be count - 1, because arrays are indexed from zero.

よくあるクラッシュの原因ですね。

Iterating Over an Array

If you need the integer index of each item as well as its value, use the enumerated() method to iterate over the array instead. For each item in the array, the enumerated() method returns a tuple composed of an integer and the item. The integers start at zero and count up by one for each item; if you enumerate over a whole array, these integers match the items’ indices. You can decompose the tuple into temporary constants or variables as part of the iteration:

    for (index, value) in shoppingList.enumerated() {
        print("Item \(index + 1): \(value)")
    }
    // Item 1: Six eggs
    // Item 2: Milk
    // Item 3: Flour
    // Item 4: Baking Powder
    // Item 5: Bananas

enumerated()便利!

Dictionaries

A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering.

いろいろ試してみました。

____________________________2016-05-02_18.21.43.png

____________________________2016-05-02_18.18.05.png

Dictionary Type Shorthand Syntax

A dictionary Key type must conform to the Hashable protocol, like a set’s value type.

キーが一意でないといけないからですね。

Accessing and Modifying a Dictionary

You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. If the dictionary contains a value for the requested key, the subscript returns an optional value containing the existing value for that key. Otherwise, the subscript returns nil:

存在しないキーでアクセスするとnilをreturnします。Arrayと違って、クラッシュはしません。

You can use subscript syntax to remove a key-value pair from a dictionary by assigning a value of nil for that key:

    airports["APL"] = "Apple International"
    // "Apple International" is not the real airport for APL, so delete it
    airports["APL"] = nil
    // APL has now been removed from the dictionary

nil を代入するとDictionaryから消せます。
let removedValue = airports.removeValue(forKey: "APL")とすると、消した値を取得することもできます。

Control Flow

Switch

Every switch statement must be exhaustive.

試しに、以下のようなコードを描いてみたら、ちゃんとコンパイルできました。すごい。

    let num = 5
    switch num {
    case Int.min..<1:
        break
    case 1..<100:
        break
    case 100...Int.max:
        break
    }

(12/6追記 @es_kumagai さんに指摘いただきました。 )
読書会時点(Xcode7.x)では、上記コードがコンパイルできていましたが、XcodeVersion 8.1 (8B62)では、以下のようなエラーになりました。
error: switch must be exhaustive, consider adding a default clause

Fallthrough

To explicitly fall through at the end of a particular switch case, use the fallthrough keyword

golangと同じキーワードですね。
switch文といいつつ実態はパターンマッチなのでデフォルトでfallthroughしないのは当然という感じ。

The fallthrough keyword does not check the case conditions for the switch case that it causes execution to fall into. The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.

試してみました。 where も無視されますね。

let i = 2
switch i {
case 2:
    print("2")
    fallthrough
case _ where i % 3 == 0:
    print("%")
default:
    break
}
// 2%とprintされる

Checking API Availability

You use an availability condition in an if or guard statement to conditionally execute a block of code, depending on whether the APIs you want to use are available at runtime. The compiler uses the information from the availability condition when it verifies that the APIs in that block of code are available.
if #available(platform name version, ..., *) { }

#available は、便利だけど構文が特殊すぎて覚えにくいですね。

Functions

Functions Without Return Values

    func greet(person: String) {
        print("Hello, \(person)!")
    }
    greet(person: "Dave")
    // Prints "Hello, Dave!"

Strictly speaking, this version of the greet(person:) function does still return a value, even though no return value is defined. Functions without a defined return type return a special value of type Void. This is simply an empty tuple, which is written as ().

Voidは空のtupleだという衝撃の事実が語られています。

Optional Tuple Return Types

An optional tuple type such as (Int, Int)? is different from a tuple that contains optional types such as (Int?, Int?). With an optional tuple type, the entire tuple is optional, not just each individual value within the tuple.

つまりこういうことですね。

var t1: (Int, Int)? = (1, 1)
t1 = nil
// t1 = (nil, 1) // error

var t2: (Int?, Int?) = (1, 1)
// t2 = nil // error
t2 = (nil, 1)

Function Argument Labels and Parameter Names

All parameters must have unique names. Although it’s possible for multiple parameters to have the same argument label, unique argument labels help make your code more readable.

つまり、こういうことが可能です。きもい。

func sayHello(to to1: String, to to2 : String) {
    print("Hello \(to1), and \(to2)!")
}
sayHello(to: "A", to: "B")

Variadic Parameters

A function may have at most one variadic parameter.

だそうです。

In-Out Parameters

In-out parameters cannot have default values, and variadic parameters cannot be marked as inout.

そりゃそうだろうという感じですね。

Nested Functions

Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope.

面白いのですが、returnする可能性があるNested functionsの型がすべて同じでないといけないので、あまり使いみちはなさそうだと思いました。

Closures

Capturing Values

As an optimization, Swift may instead capture and store a copy of a value if that value is not mutated by a closure, and if the value is not mutated after the closure is created.
Swift also handles all memory management involved in disposing of variables when they are no longer needed.

最適化として、キャプチャの代わりに値のコピーを取得して格納することがあります。

If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles.

循環参照の話ですね。

Escaping Closures

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

escapingについてに説明です。関数の引数でクロージャを受け渡すときに escaping が必要になる条件について書かれています。
Xcodeがエラーを出してくれるので、便利ですね。

スクリーンショット 2016-12-04 18.10.20.png

Autoclosures

カスタム演算子を作るときによく使用されるAutoclosuresについて丁寧に説明されています。

Enumerations

Enumeration Syntax

Unlike C and Objective-C, Swift enumeration cases are not assigned a default integer value when they are created.

各caseに対応するIntが欲しい場合は、明示する必要があります。
ただし、継承元の型がIntFloatなどが指定されている場合は、デフォルトのrawValueが紐付けられるようです。

enum Num: Int {
    case a, b, c
}
print(Num.a.rawValue)
// 0 とprintされる
print(Num.b.rawValue)
// 1 とprintされる
print(Num.c.rawValue)
// 2 とprintされる
enum Str: String {
    case a, b, c
}
print(Str.a.rawValue)
// a とprintされる
print(Str.b.rawValue)
// b とprintされる
print(Str.c.rawValue)
// c とprintされる

Associated Values

Swiftでよく利用されるEnumのAssociated Valuesについて、丁寧に説明されています。

Classes and Structures

Identity Operators

Because classes are reference types, it is possible for multiple constants and variables to refer to the same single instance of a class behind the scenes. (The same is not true for structures and enumerations, because they are always copied when they are assigned to a constant or variable, or passed to a function.)
It can sometimes be useful to find out if two constants or variables refer to exactly the same instance of a class. To enable this, Swift provides two identity operators:
・Identical to (===)
・Not identical to (!==)
Use these operators to check whether two constants or variables refer to the same single instance:

===は厳密比較ではなく、参照先が同じであるかのチェックです。

Choosing Between Classes and Structures

As a general guideline, consider creating a structure when one or more of these conditions apply:
・The structure’s primary purpose is to encapsulate a few relatively simple data values.
・It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.
・Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
・ The structure does not need to inherit properties or behavior from another existing type.

Classで実装すべきか、Structで実装すべきかの一般的な判断ポイントが書かれています。

Assignment and Copy Behavior for Strings, Arrays, and Dictionaries

In Swift, many basic data types such as String, Array, and Dictionary are implemented as structures. This means that data such as strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.

Swiftの基本のデータ型は、Structで実装されていて、新しい定数や変数に割り当てるときや、メソッドに受け渡すときにコピーが発生します。

The description above refers to the “copying” of strings, arrays, and dictionaries. The behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.

Swiftの最適化により、本当に必要なときのみコピーを実施するようになります。

Properties

Property Observers

The willSet and didSet observers of superclass properties are called when a property is set in a subclass initializer, after the superclass initializer has been called. They are not called while a class is setting its own properties, before the superclass initializer has been called.

willSetdidSetが呼ばれないことがあるパターンについての説明ですね。

If you pass a property that has observers to a function as an in-out parameter, the willSet and didSet observers are always called. This is because of the copy-in copy-out memory model for in-out parameters: The value is always written back to the property at the end of the function.

in-out のパラメータに受け渡すと、willSetdidSetが呼び出されます。

Global and Local Variables

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.
Local constants and variables are never computed lazily.

グローバルな定数と変数は常にLazyに評価されます。lazyと書く必要はありません。

Type Properties

You can also define properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties, no matter how many instances of that type you create. These kinds of properties are called type properties.

インスタンスが何個あっても、型プロパティは1つのコピーしか持ちません。

Methods

Modifying Value Types from Within Instance Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method.

mutating の説明です。

Assigning to self Within a Mutating Method

Mutating methods can assign an entirely new instance to the implicit self property. The Point example shown above could have been written in the following way instead:

    struct Point {
        var x = 0.0, y = 0.0
        mutating func moveBy(x deltaX: Double, y deltaY: Double) {
            self = Point(x: x + deltaX, y: y + deltaY)
        }
    }

mutatingは過激ですね。

Type Methods

Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type. This means that you can use self to disambiguate between type properties and type method parameters, just as you do for instance properties and instance method parameters.

型メソッド内のselfは型自体を表します。

Subscripts

You can define multiple subscripts for a single type, and the appropriate subscript overload to use is selected based on the type of index value you pass to the subscript. Subscripts are not limited to a single dimension, and you can define subscripts with multiple input parameters to suit your custom type’s needs.

1つの型に複数のsubscriptを定義できるんですね。

Inheritance

Classes can also add property observers to inherited properties in order to be notified when the value of a property changes. Property observers can be added to any property, regardless of whether it was originally defined as a stored or computed property.

Overriding Properties

You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property.

overrideでread-onlyをread-writeにすることはできるけど、逆はできないんですね。

If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.someProperty from the getter, where someProperty is the name of the property you are overriding.

setterだけoverrideすることはできないんですね。

Initialization

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

インスタンス生成時には、すべてのストアドプロパティは初期値を与えられている必要があります。

When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.

property observersのタイミング、地味にハマりそうですね。

Optional Property Types

Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.

Optional型のプロパティは自動的にnilで初期化されます。

Initializer Delegation for Value Types

If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.

ここまで指定してくれるなら、golangのgo fmtみたいにデフォルトでコードフォーマット提供してほしいなと思いました。

Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. This constraint prevents a situation in which additional essential setup provided in a more complex initializer is accidentally circumvented by someone using one of the automatic initializers.

値型にcustom initializerを作るとdefault initializerにアクセスできなくなります。

Automatic Initializer Inheritance

As mentioned above, subclasses do not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

初期化子の継承のルールです。

Deinitialization

Deinitializers are called automatically, just before instance deallocation takes place. You are not allowed to call a deinitializer yourself. Superclass deinitializers are inherited by their subclasses, and the superclass deinitializer is called automatically at the end of a subclass deinitializer implementation. Superclass deinitializers are always called, even if a subclass does not provide its own deinitializer.

deinit を自分で呼んではいけません。

Automatic Reference Counting

Weak References

Property observers aren’t called when ARC sets a weak reference to nil.

またProperty observersの罠ですね。

Optional Chaining

Accessing Subscripts of Optional Type

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72

subscriptもOptional Chainingできるんですね。

Error Handling

Representing and Throwing Errors

In Swift, errors are represented by values of types that conform to the Error protocol. This empty protocol indicates that a type can be used for error handling.

Errorプロトコルに準拠していると、throwできます。

Handling Errors

Error handling in Swift resembles exception handling in other languages, with the use of the try, catch and throw keywords. Unlike exception handling in many languages—including Objective-C—error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive. As such, the performance characteristics of a throw statement are comparable to those of a return statement.

Swiftは、パフォーマンスのために、throw時にはbacktraceを取りません。

Type Casting

You can also use type casting to check whether a type conforms to a protocol, as described in Checking for Protocol Conformance.

Type Castingは、Protocolに準拠しているかどうかのチェックにも使えます。

Type Casting for Any and AnyObject

Swift provides two special types for working with nonspecific types:
・ Any can represent an instance of any type at all, including function types.
・ AnyObject can represent an instance of any class type.

AnyとAnyObjectの説明です。

Checking Type

Use the type check operator (is) to check whether an instance is of a certain subclass type.

isはインスタンスが任意のクラスを継承しているかどうかを判定します。

Extensions

Extensions can add new functionality to a type, but they cannot override existing functionality.

Extensionsでoverrideはできません。

Computed Properties

Extensions can add new computed properties, but they cannot add stored properties, or add property observers to existing properties.

Extensionsでstored propertiesやproperty observersを追加することはできません。

Nested Types

Extensions can add new nested types to existing classes, structures, and enumerations:

    extension Int {
        enum Kind {
            case negative, zero, positive
        }
        var kind: Kind {
            switch self {
            case 0:
                return .zero
            case let x where x > 0:
                return .positive
            default:
                return .negative
            }
        }
    }

これは便利そうですね。

Protocols

Property Requirements

A protocol can require any conforming type to provide an instance property or type property with a particular name and type. The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettable and settable.

protocolでは、stored propertyを追加することはできませんが、任意の名前のpropertyの実装を強制することができます。メソッドやイニシャライザについても同様です。

Protocols as Types

Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.

Because it is a type, you can use a protocol in many places where other types are allowed, including:

・ As a parameter type or return type in a function, method, or initializer

・ As the type of a constant, variable, or property

・ As the type of items in an array, dictionary, or other container

プロトコルは型なので、普通の型と同様の使い方ができます。

Class-Only Protocols

You can limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols:

    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
        // class-only protocol definition goes here
    }

クラスだけに適合可能なprotocolを作ることができます。

Protocol Composition

It can be useful to require a type to conform to multiple protocols at once. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need to, separating them by ampersands (&).

複数のプロトコルに準拠させることができます。多重継承のようなことができます。

Optional Protocol Requirements

You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol’s definition. Optional requirements are available so that you can write code that interoperates with Objective-C. Both the protocol and the optional requirement must be marked with the @objc attribute. Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes. They can’t be adopted by structures or enumerations.

    @objc protocol CounterDataSource {
        @objc optional func increment(forCount count: Int) -> Int
        @objc optional var fixedIncrement: Int { get }
    }

プロトコルに準拠しても、実装を矯正しないようにするには @objcoptional を付加します。

Generics

Naming Type Parameters

Always give type parameters upper camel case names (such as T and MyTypeParameter) to indicate that they are a placeholder for a type, not a value.

型パラメータはアッパーキャメルケースにしましょう。

Type Constraint Syntax

You write type constraints by placing a single class or protocol constraint after a type parameter’s name, separated by a colon, as part of the type parameter list. The basic syntax for type constraints on a generic function is shown below (although the syntax is the same for generic types):

    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
        // function body goes here
    }

型制約の構文です。

Access Control

Indeed, if you are writing a single-target app, you may not need to specify explicit access control levels at all.

単一ターゲットのアプリを開発する際は、特にaccess control levelsを明示しなくても良いです。

Modules and Source Files

Swift’s access control model is based on the concept of modules and source files.

Swiftのアクセス制御モデルは、モジュールとソースファイルの概念に基づいています。

A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword.

モジュールは1つコードの配布単位です。

Access Levels

Marking a class as open explicitly indicates that you’ve considered the impact of code from other modules using that class as a superclass, and that you’ve designed your class’s code accordingly.

openを指定するときは、他のモジュールへの影響を考慮しましょう。

Custom Types

A public type defaults to having internal members, not public members. If you want a type member to be public, you must explicitly mark it as such. This requirement ensures that the public-facing API for a type is something you opt in to publishing, and avoids presenting the internal workings of a type as public API by mistake.

public型のメンバーもデフォルトでinternalです。

こちらからは以上です!