LoginSignup
53
42

More than 5 years have passed since last update.

[Xcode10] Swift4.2でパワーアップしたことについてをまとめました[日本語訳]

Last updated at Posted at 2018-06-16

はじめに

この記事は勉強がてら下記のページの英語の記事を翻訳したものである。

What’s New in Swift 4.2?

翻訳の許可を頂いてます。

Swift4.2で新しくなったものは?

グッドニュース: Swift4.2は今Xcode10βで利用できます!このリリースはSwift4.1の大切なものを更新しABIの安定性にとっての準備として言語の改善します。

このチュートリアルはSwift4.2のもっとも重要な変更をカバーしています。
これはXcode10が必須で始める前に最新のXcodeβをダウンロードしてインストールしてください。

Xcode10のDownload Link

始める前に

Swift 4.2はSwift 4.1とソース互換ですが、他のリリースとのバイナリ互換性はありません。Appleは、Swift5でABIの安定性を達成するための中間ステップとして、Swift 4.2を設計しました。これは、異なるSwiftバージョンでコンパイルされたアプリケーションとライブラリのバイナリ互換性を有効にするはずです。ABIの機能は、最終的なABIに統合される前にコミュニティからのフィードバックに多くの時間を与えます。

このチュートリアルのセクションには、[SE-0001]のようなSwift Evolutionの提案番号が含まれています。各提案のリンクされたタグをクリックすると、各変更の詳細を調べることができます。

playgroundでこの変更を試してみると、このチュートリアルを最大限に活用できます。 Xcode10を起動し、ファイル→新規作成→playgroundに進みます。 プラットフォーム用はiOSでテンプレートはBlankを選択してください。 あなたの好きな場所に名前を付けて、どこにでも保存してください。 今すぐ始めましょう!

言語の改善について

このリリースには、乱数ジェネレータ、動的メンバルックアップなど、いくつかの言語機能があります。

Generating Random Numbers (乱数ジェネレータ)

Swift 4.1は以下のスニペットのように、CのAPIをインポートし乱数を生成します。

sample.swift
let digit = Int(arc4random_uniform(10))

arc4random_uniform(_ :)は0と9の間のランダムな数字を返しました。Foundationをインポートする必要があり、Linuxでは動作しませんでした。 一方、すべてのLinuxベースのアプローチでは、moduloバイアスが導入されました。これは、特定の数が他の数より頻繁に生成されることを意味しました。

Swift 4.2は、ランダムなAPIを標準ライブラリ[SE-0202]に追加することで、これらの問題を解決しています。

sample.swift
// 1  
let digit = Int.random(in: 0..<10)

// 2
if let anotherDigit = (0..<10).randomElement() {
  print(anotherDigit)
} else {
  print("Empty range.")
}

// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()

これが意味しているものは
1. 範囲からランダムな数字を生成するには、random(in :)を使用します。
2.  randomElement()はrangeが空の場合はnilを返します。だから、if letを使って、返されたIntをアンラップしましょう。
3. random(in :)はランダムなDoubleやFloat、またはCGFloatを生成します。random()は生成し、ランダムなBoolを返します。

Swift 4.1ではArraysからランダムな値を生成するためにCの関数も使用されていました。

sample.swift
let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"]
let index = Int(arc4random_uniform(UInt32(playlist.count)))
let song = playlist[index]

Swift 4.1では、playlistから有効なindexを生成し、対応するsongを返すためにarc4random_uniform(_ :)を使用します。 このやり方では、IntとUInt32の間でキャストする必要がありましたし、前述のすべての問題もありました。

Swift 4.2では、より簡単なアプローチが採用されています。

sample.swift

if let song = playlist.randomElement() {
  print(song)
} else {
  print("Empty playlist.")
}

playlistが空の場合はrandomElement()はnilを返します。だから返されたString?をアンラップしましょう。

Swift 4.1にはコレクションのシャッフルアルゴリズムが含まれていなかったので、意図した結果を得るためにはroundabout法を使用しなければなりませんでした。

sample.swift
// 1
let shuffledPlaylist = playlist.sorted{ _, _ in arc4random_uniform(2) == 0 }

// 2
var names = ["Cosmin", "Oana", "Sclip", "Nori"]
names.sort { _, _ in arc4random_uniform(2) == 0 }

このコードでは次のことを行います:
1. playlistのシャッフル順を決定し、sorted( _ : _ : )されたshuffledPlaylistを返すためにarc4random_uniform(_ :)を使用します。
2. これで、以前の手法を使ってsort( _ : _ : )でnameをシャッフルします。

Swift 4.2は、より効率的で、間違いなくよりエレガントなシャッフルアルゴリズムを提供します。

sample.swift
let shuffledPlaylist = playlist.shuffled()
names.shuffle()

4.2では、shuffled()を使用してシャッフルされたplaylistを作成し、shuffle()でそのnameをシャッフルします。

ブーム!

Dynamic Member Lookup (動的メンバー検索)

Swift 4.1では、カスタムな添え字の呼び出しに次の角括弧の構文を使用しました。

sample.swift

class Person {
  let name: String
  let age: Int
  private let details: [String: String]

  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }

  subscript(key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}

let details = ["title": "Author", "instrument": "Guitar"]
let me = Person(name: "Cosmin", age: 32, details: details)
me["info"]   // "Cosmin is 32 years old."
me["title"]  // "Author"

この場合、サブスクリプトはPersonのnameとageに基づいて、privateなデータストア、またはカスタムメッセージからコンテンツを返します。

Swift 4.2では、[SE-0195]の代わりに、添字のためにドット構文を提供するdynamic member lookupを使用します。

sample.swift

// 1
@dynamicMemberLookup
class Person {
  let name: String
  let age: Int
  private let details: [String: String]

  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }

  // 2
  subscript(dynamicMember key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}


// 3
me.info   // "Cosmin is 32 years old." 
me.title  // "Author"

順を追って説明します。

  1. Person@dynamicMemberLookupとマークすると、カスタムな添字のドット構文が有効になります。
  2. クラスのsubscript(dynamicMember :)を実装することで@dynamicMemberLookupに準拠します。
  3. ドット構文を使用すると以前に実装された添字を呼び出します。

コンパイラは、ランタイム時(実行時)に添字の呼び出しを動的に(dynamically)評価します。これにより、PythonやRubyなどのスクリプト言語と同じようにタイプセーフなコードを書くことができます。

Dynamic member loopupだとクラスプロパティが混乱することはありません:

sample.swift
me.name // "Cosmin"
me.age // 32

この場合は、添字の代わりにnameとageを呼び出スタメにドット構文を使用します。

さらに、派生クラスはdynamic member lookupを基本メンバーから継承します。

sample.swift

@dynamicMemberLookup
class Vehicle {
  let brand: String
  let year: Int

  init(brand: String, year: Int) {
    self.brand = brand
    self.year = year
  }

  subscript(dynamicMember key: String) -> String {
    return "\(brand) made in \(year)."
  }
}

class Car: Vehicle {}

let car = Car(brand: "BMW", year: 2018)
car.info  // "BMW made in 2018."

すべてのCarVehicleVehicle@dynamicMemberLookupを実装しているため、Carの添字の呼び出しにドット構文が使用できます。

プロトコルエクステンション(プロトコル拡張)を使用して、既存の型に動的メンバー参照(dynamic member lookup)を追加できます。

sample.swift

// 1
@dynamicMemberLookup
protocol Random {}

// 2
extension Random {
  subscript(dynamicMember key: String) -> Int {
    return Int.random(in: 0..<10)
  }
}

// 3
extension Int: Random {}

// 4
let number = 10
let randomDigit = String(number.digit)
let noRandomDigit = String(number).filter { String($0) != randomDigit }

順を追って説明すると

  1. その添え字のドット構文を有効にするために、@dynamicMemberLookupを使ってRandomに注釈を付けます。
  2. プロトコルを拡張し、subscript(dynamicMember :)を実装することで@dynamicMemberLookupに準拠させます。 添字はrandom(in :)を使用して0から9の間のランダムな数字を返します。
  3. Intを拡張し、それをRandomに適合させます。
  4. 乱数を生成し、numberからそれをフィルタリングするためにドット構文を使用します。

Enumeration Cases Collections (Enumeration Casesコレクション)

Swift 4.1ではデフォルトでenumケースのコレクションにアクセスできませんでした。 これは、次のようなやや面白い解決策でした:

sample.swift

enum Seasons: String {
  case spring = "Spring", 
       summer = "Summer", 
       autumn = "Autumn", 
       winter = "Winter"
}

enum SeasonType {
  case equinox
  case solstice
}

let seasons = [Seasons.spring, .summer, .autumn, .winter]
for (index, season) in seasons.enumerated() {
  let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
  print("\(season.rawValue) \(seasonType).")
}

ここでは、SeasonsのケースをSeasonsに追加し、配列をループして各seasonのnameとtypeを取得します。 しかし、Swift 4.2はあなたをより良くすることができます!

Swift 4.2は列挙ケースArrayを列挙に追加します[SE-0194]

sample.swift
// 1
enum Seasons: String, CaseIterable {
  case spring = "Spring", 
       summer = "Summer", 
       autumn = "Autumn", 
       winter = "Winter"
}

enum SeasonType {
  case equinox
  case solstice
}

// 2
for (index, season) in Seasons.allCases.enumerated() {
  let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
  print("\(season.rawValue) \(seasonType).")
}

Swift 4.2で同じことを達成する方法は次のとおりです。

  1. enumケースのArrayを作成するためにSeasonsCaseIterableに準拠させます。
  2. AllCasesをループして、各seasonnametypeをprintします。

enumケースArrayに特定のケースのみを追加するオプションがあります。

sample.swift
enum Months: CaseIterable {
  case january, february, march, april, may, june, july, august, september, october, november, december          

  static var allCases: [Months] {
    return [.june, .july, .august]
  }
}

ここでは、AllCasesにsummerのMonthsだけを追加します。これは、一番日にちが一番美しい日ですから!

New Sequence Methods (新しいシーケンスメソッド)

Swift 4.1 は特定の要素の最初のindexまたは特定の条件を満たす最初の要素のいずれかを決定するSequenceメソッドを定義していました:

sample.swift
let ages = ["ten", "twelve", "thirteen", "nineteen", "eighteen", "seventeen", "fourteen",  "eighteen", 
            "fifteen", "sixteen", "eleven"]

if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
   let firstIndex = ages.index(where: { $0.hasSuffix("teen") }), 
   let firstMajorIndex = ages.index(of: "eighteen") {
  print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
  print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

Swift 4.1のやり方は、まずagesから十代の若者のageを見つけるためにfirst(where:)を使い、最初の十代の若者のindexのためにindex(where:)を使い、18歳の最初のティーンエイジャーのindexのためにindex(of:) を使います。

Swift 4.2は、一貫性のためにこれらのメソッド名を変更しました[SE-0204]

sample.swift

if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
   let firstIndex = ages.firstIndex(where: { $0.hasSuffix("teen") }), 
   let firstMajorIndex = ages.firstIndex(of:  "eighteen") {
  print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
  print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

index(where:)firstIndex(where:)になり、index(of :)first(where :)と一貫するためにfirstIndex(of :)になりました。

Swift 4.1では、特定の要素の最後のindex、または与えられた検索条件に一致した最後の要素のいずれかを見つけるためのコレクションメソッドも定義していませんでした。 4.1でこれを処理する方法は次のとおりです。

(Swift4.1)

sample.swift

// 1
let reversedAges = ages.reversed()

// 2
if let lastTeen = reversedAges.first(where: { $0.hasSuffix("teen") }), 
   let lastIndex = reversedAges.index(where: { $0.hasSuffix("teen") })?.base, 
   let lastMajorIndex = reversedAges.index(of: "eighteen")?.base {
  print("Teenager number \(lastIndex) is \(lastTeen) years old.")
  print("Teenager number \(lastMajorIndex) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

これをセクションで見ると:

  1. reversed()を使用してagesの逆のバージョンを作成します。
  2. reversedAgesの最後のティーンエイジャーの年齢を決定するためにfirst(where :)を使用します。最後のティーンエイジャーのindexのためにindex(where :)、18歳の最後のティーンエイジャーのindexのためのindex(of :)を使います。

Swift 4.2では、対応するSequenceメソッドが追加され、上に戻ると以下のようになります。

(Swift4.2)

sample.swift

if let lastTeen = ages.last(where: { $0.hasSuffix("teen") }), 
   let lastIndex = ages.lastIndex(where: { $0.hasSuffix("teen") }), 
   let lastMajorIndex = ages.lastIndex(of: "eighteen") {
  print("Teenager number \(lastIndex + 1) is \(lastTeen) years old.")
  print("Teenager number \(lastMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

last(where :)lastIndex(where :)およびlastIndex(of :)を使用すると、agesにある以前の要素と特定の指標を見つけることができます。

Testing Sequence Elements (シーケンス要素のテスト)

Swift 4.1にはないかなり単純なルーティンは、Sequence内のすべての要素が一定の条件を満たすかどうかをチェックする方法です。 ただし、ここではすべての要素が偶数(=even)であるかどうかを判断する必要があります。

sample.swift
let values = [10, 8, 12, 20]
let allEven = !values.contains { $0 % 2 == 1 }

いいかげんでしょう! Swift 4.2はこの欠落したメソッドをシーケンス[SE-0207]に追加します:

sample.swift
let allEven = values.allSatisfy { $0 % 2 == 0 }

ずっといいですね! これにより、コードが簡単になり可読性が向上します。

Conditional Conformance Updates (条件付き適合性の更新)

Swift 4.2では、拡張ライブラリと標準ライブラリ[SE-0143]にいくつかの条件付き適合性が追加されました。

Conditional conformance in extensions (拡張機能の条件適合)

Swift 4.1は、extensionでEquatableに条件付きの準拠を合成できませんでした。 例として、次のSwift 4.1スニペットを参照してください。

sample.swift
// 1
struct Tutorial : Equatable {
  let title: String
  let author: String
}

// 2
struct Screencast<Tutorial> {
  let author: String
  let tutorial: Tutorial
}

// 3
extension Screencast: Equatable where Tutorial: Equatable {
  static func ==(lhs: Screencast, rhs: Screencast) -> Bool {
    return lhs.author == rhs.author && lhs.tutorial == rhs.tutorial
  }
}

// 4
let swift41Tutorial = Tutorial(title: "What's New in Swift 4.1?", author: "Cosmin Pupăză")
let swift42Tutorial = Tutorial(title: "What's New In Swift 4.2?", author: "Cosmin Pupăză")
let swift41Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift41Tutorial)
let swift42Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift42Tutorial)
let sameScreencast = swift41Screencast == swift42Screencast
  1. TutorialEquatableに適合させます。
  2. Screencastをジェネリックにするのは、ウェブサイトの作者が公開されたtutorialsのscreencastsをベースにしているからです。
  3. Tutorialがそうである限り、ScreencastEquatableに準拠しているので、screencastsのために==(lhs:rhs :)を実装します。
  4. あなたが宣言した条件付き適合(conditional conformance)のため、screencastsを直接比較します。

Swift 4.2では、Equatableの条件付き適合のデフォルト実装がextensionに追加されています。

sample.swift
extension Screencast: Equatable where Tutorial: Equatable {}

この機能は、extensionのHashableおよびCodableの準拠にも適用されます。

sample.swift
// 1
struct Tutorial: Hashable, Codable {
  let title: String
  let author: String
}

struct Screencast<Tutorial> {
  let author: String
  let tutorial: Tutorial
}

// 2
extension Screencast: Hashable where Tutorial: Hashable {}
extension Screencast: Codable where Tutorial: Codable {}

// 3
let screencastsSet: Set = [swift41Screencast, swift42Screencast]
let screencastsDictionary = [swift41Screencast: "Swift 4.1", swift42Screencast: "Swift 4.2"]

let screencasts = [swift41Screencast, swift42Screencast]
let encoder = JSONEncoder()
do {
  try encoder.encode(screencasts)
} catch {
  print("\(error)")
  1. TutorialHashableCodableの両方に準拠させます。
  2. Tutorialであれば、ScreencastHashableCodableに拘束します。
  3. SetとDictionaryにScreencastsを追加し、それらをエンコードします。
Conditional conformance runtime queries (条件適合ランタイムクエリ)

Swift 4.2は、条件適合の動的クエリを実装します。 これは実際には次のコードで見ることができます:

sample.swift
// 1
class Instrument {
  let brand: String

  init(brand: String = "") {
    self.brand = brand
  }
}

// 2
protocol Tuneable {
  func tune()
}

// 3
class Keyboard: Instrument, Tuneable {
  func tune() {
    print("\(brand) keyboard tuning.")
  }
}

// 4
extension Array: Tuneable where Element: Tuneable {
  func tune() {
    forEach { $0.tune() }
  }
}

// 5
let instrument = Instrument()
let keyboard = Keyboard(brand: "Roland")
let instruments = [instrument, keyboard]

// 6
if let keyboards = instruments as? Tuneable {
  keyboards.tune()
} else {
  print("Can't tune instrument.")
}

上記では何をしているのかというと:

  1. 特定のブランドでInstrumentを定義します。
  2. チューニング可能なすべてのInstrumentsに対してTuneableを宣言します。
  3. キーボードの標準チューニングを戻すために、Keyboardtune()をオーバーライドします。
  4. Elementが行う限り、Tuneableに準拠するためにArrayを制約するwhereを使用します。
  5. InstrumentKeyboardInstrumentに追加します。
  6. InstrumentTuneableが実装されているかどうかを確認し、テストが成功した場合は調整します。 この例では、Instrumentタイプが調整可能ではないため、arrayをTuneableにキャストできません。 2つのkeyboardのArrayを作成したら、テストは合格し、keyboardは調整されます。
Hashable conditional conformance improvements in the standard library(標準ライブラリにおけるHashable条件適合の改善)

Optional、配列、辞書、およびRangeは、それらの要素がHashableの時でも、Swift 4.2ではHashableです。

sample.swift
struct Chord: Hashable {
  let name: String
  let description: String?
  let notes: [String]
  let signature: [String: [String]?]
  let frequency: CountableClosedRange<Int>
}

let cMajor = Chord(name: "C", description: "C major", notes: ["C", "E",  "G"], 
                   signature: ["sharp": nil,  "flat": nil], frequency: 432...446)
let aMinor = Chord(name: "Am", description: "A minor", notes: ["A", "C", "E"], 
                   signature: ["sharp": nil, "flat": nil], frequency: 440...446)
let chords: Set = [cMajor, aMinor]
let versions = [cMajor: "major", aMinor: "minor"]

あなたは、cMajorcMinorchordsversionsに追加します。 String?, [String][String:[String]?]、およびCountableClosedRangeHashableではないため、これは4.2より以前のものでは不可能でした。

Hashable Improvements

Swift 4.1で、あるクラスのカスタムハッシュ関数を実装する次の例を考えてみましょう。

sample.swift

class Country: Hashable {
  let name: String
  let capital: String

  init(name: String, capital: String) {
    self.name = name
    self.capital = capital
  }

  static func ==(lhs: Country, rhs: Country) -> Bool {
    return lhs.name == rhs.name && lhs.capital == rhs.capital
  }

  var hashValue: Int {
    return name.hashValue ^ capital.hashValue &* 16777619
  }
}

let france = Country(name: "France", capital: "Paris")
let germany = Country(name: "Germany", capital: "Berlin")
let countries: Set = [france, germany]
let countryGreetings = [france: "Bonjour", germany: "Guten Tag"]

Hashableなので、ここでSetやDictionaryにcountriesを追加できます。 しかし、hashValueの実装は理解が難しく、信頼できないソース値に対してはかなり効率的ではありません。

Swift 4.2は、汎用ハッシュ関数[SE-0206]を定義することでこれを修正しています。

sample.swift
class Country: Hashable {
  let name: String
  let capital: String

  init(name: String, capital: String) {
    self.name = name
    self.capital = capital
  }

  static func ==(lhs: Country, rhs: Country) -> Bool {
    return lhs.name == rhs.name && lhs.capital == rhs.capital
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(name)
    hasher.combine(capital)
  }
}

ここでは、CountryでhashValueをhash(into :)に置き換えました。 この関数はcombine()を使用して、クラスプロパティをhasherに送ります。 実装が簡単で、以前のすべてのバージョンよりもパフォーマンスが向上します。

Removing Elements From Collections (コレクションから要素を削除する)

コレクションから特定の要素のすべてのobjectを削除したいことがよくあります。 Swift 4.1でfilter(_ :)を使用してこれを行う方法は次のとおりです。

(Swift4.1)

sample.swift

var greetings = ["Hello", "Hi", "Goodbye", "Bye"]
greetings = greetings.filter { $0.count <= 3 }

文字列の短いgreetingsだけを返すようにgreetingsをfilterします。これは元のArray(greetings)には影響しませんので、結果をgreetingsに戻す必要があります。

Swift4.2では、これにremoveAll(_:)が追加されました。

(Swift4.2)

sample.swift
greetings.removeAll { $0.count > 3 }

Toggling Boolean States (Boolステートのトグル)

Bool型をトグルする! スイフト4.1ではこのような何かを持っているクラス?はない:

sample.swift

extension Bool {
  mutating func toggle() {
    self = !self
  }
}

var isOn = true
isOn.toggle()

Swift 4.2は、[SE-0199]の下でBoolにtoggle()を追加しています。

New Compiler Directives (新しいコンパイラ指令)

Swift 4.2はあなたのコード[SE-0196]の問題を通知するコンパイラ指令を定義しています:

(Swift4.2)

sample.swift

// 1
#warning("There are shorter implementations out there.")

let numbers = [1, 2, 3, 4, 5]
var sum = 0
for number in numbers {
  sum += number
}
print(sum)

// 2
#error("Please fill in your credentials.")

let username = ""
let password = ""
switch (username.filter { $0 != " " }, password.filter { $0 != " " }) {
  case ("", ""):
    print("Invalid username and password.")
  case ("", _):
    print("Invalid username.")
  case (_, ""):
    print("Invalid password.")
  case (_, _):
    print("Logged in succesfully.")
}     

これでどう機能するのかというと

  1. #warningは、numbersで要素を追加するための機能的アプローチが命令的なアプローチよりも短いことをリマンダーさせます。
  2. #errorを使用して、他の開発者にログインする前にユーザー名とパスワードを入力することを強制させます。

New Pointer Functions (新しいポインタの関数)

Swift 4.1では、withUnsafeBytes(of:_ :)withUnsafePointer(to:_ :)はmutableな変数に対してのみ機能しました:

(Swift4.1)

sample.swift

let value = 10
var copy = value
withUnsafeBytes(of: &copy) { pointer in print(pointer.count) }
withUnsafePointer(to: &copy) { pointer in print(pointer.hashValue) }

両方の機能を動作させるには、valueのcopyを作成しなければなりませんでした。 Swift 4.2ではこれらの関数が定数にオーバーロードされるため、値を保存する必要はなくなりました[SE-0205]

(Swift4.2)

sample.swift
withUnsafeBytes(of: value) { pointer in print(pointer.count) }
withUnsafePointer(to: value) { pointer in print(pointer.hashValue) }

Memory Layout Updates (メモリーレイアウトの更新)

Swift 4.2は、格納されたプロパティのメモリレイアウトを照会するためにkey pathsを使用します[SE-0210]。 それがどのように動作するのかというと:

(Swift4.2)

sample.swift

// 1
struct Point {
  var x, y: Double
}

// 2
struct Circle {
  var center: Point
  var radius: Double

  var circumference: Double {
    return 2 * .pi * radius
  }

  var area: Double {
    return .pi * radius * radius
  }
}

// 3
if let xOffset = MemoryLayout.offset(of: \Circle.center.x), 
   let yOffset = MemoryLayout.offset(of: \Circle.center.y), 
   let radiusOffset = MemoryLayout.offset(of: \Circle.radius) {
  print("\(xOffset) \(yOffset) \(radiusOffset)")
} else {
  print("Nil offset values.")
}

// 4
if let circumferenceOffset = MemoryLayout.offset(of: \Circle.circumference), 
   let areaOffset = MemoryLayout.offset(of: \Circle.area) {
  print("\(circumferenceOffset) \(areaOffset)")
} else {
  print("Nil offset values.")
}

ステップバイステップで説明します:

  1. ポイントの水平座標と垂直座標を定義します。
  2. 円の中心円周面積半径を宣言します。
  3. キーパスを使用して、円のストアドプロパティのオフセットを取得します。
  4. サークルの計算されたプロパティのオフセットは、インラインで保存されていないため、nilを返します。

Inline Functions in Modules (モジュールのインライン関数)

Swift 4.1では、独自のモジュールでインライン関数を宣言できませんでした。 View▸Navigators▸Show Project Navigatorで進み、Sourcesを右クリックし、New Fileを選択します。 ファイル名をFactorialKit.swiftに変更し、その内容を以下のコードブロックに置き換えます。

FactorialKit.swift
public class CustomFactorial {
  private let customDecrement: Bool

  public init(_ customDecrement: Bool = false) {
    self.customDecrement = customDecrement
  }

  private var randomDecrement: Int {
    return arc4random_uniform(2) == 0 ? 2 : 3
  }

  public func factorial(_ n: Int) -> Int {
    guard n > 1 else {
      return 1
    }
    let decrement = customDecrement ? randomDecrement : 1
    return n * factorial(n - decrement)
  }
}

階乗実装のカスタムバージョンを作成しました。 playgroundに戻って、下のコードを一番下に追加してください:

playground.swift
let standard = CustomFactorial()
standard.factorial(5)
let custom = CustomFactorial(true)
custom.factorial(5)

ここでは、デフォルト階乗と乱数の両方を生成しています。 Swift 4.2 [SE-0193]にインライン展開するとモジュール間の関数がより効率的になりますので、FactorialKit.swiftに戻り、CustomFactorialを次のように置き換えてください:

FactorialKit.swift

public class CustomFactorial {
  @usableFromInline let customDecrement: Bool

  public init(_ customDecrement: Bool = false) {
    self.customDecrement = customDecrement
  }

  @usableFromInline var randomDecrement: Int {
    return Bool.random() ? 2 : 3
  }

  @inlinable public func factorial(_ n: Int) -> Int {
    guard n > 1 else {
      return 1
    }
    let decrement = customDecrement ? randomDecrement : 1
    return n * factorial(n - decrement)
  }
}

これが行なっているものは
1. customDecrementrandomDecrementの両方をinternalとして設定し、インライン階乗実装で使用するので、それらを@usableFromInlineとしてマークします。
2. factorial(_ :)@inlinableを注釈してインラインにします。 これは、関数をpublicとして宣言したために可能です。

Miscellaneous Bits and Pieces (その他のbitとpiece)

翻訳予定なし

Removing Implicitly Unwrapped Optionals (暗黙的アンラップ型の削除)

Swift 4.1では、ネスト型で暗黙的アンラップ型を使用できます。

(Swift4.1)

sample.swift

let favoriteNumbers: [Int!] = [10, nil, 7, nil]
let favoriteSongs: [String: [String]!] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], 
                                          "Oana": nil] 
let credentials: (usermame: String!, password: String!) = ("Cosmin", nil)

Swift 4.2は、それらをArray、Dictionary、タプルから削除します[SE-0054]

(Swift4.2)

sample.swift

let favoriteNumbers: [Int?] = [10, nil, 7, nil]
let favoriteSongs: [String: [String]?] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], 
                                          "Oana": nil] 
let credentials: (usermame: String?, password: String?) = ("Cosmin", nil)

53
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
53
42