[Swift5]Codableソースコード解析①

# Codableから説明  
### 関連するファイルは二つあります：
1. gybにユーザーに公開しているprotocolおよびCodabelを実現しているSwiftのタイプ  
2. JSONEncoder.swift。JSONEncoderとJSONDecoderを実現しているファイルです。  
続きましては、Codableの定義はここです：  

```swift:qiita.swift
public typealias Codable = Encodable & Decodable
```  

# Encodable
Encodableのタイプはなんでしょうか？実際、`encode(to:)`メソッドを提供しているだけです。  

```swift:qiita.swift
public protocol Encodable {
  /// Encodes this value into the given encoder.
  ///
  /// If the value fails to encode anything, `encoder` will encode an empty
  /// keyed container in its place.
  ///
  /// This function throws an error if any values are invalid for the given
  /// encoder's format.
  ///
  /// - Parameter encoder: The encoder to write data to.
  func encode(to encoder: Encoder) throws
}
```  

ここまで、三つの質問を思い出すと：
1.SwiftがサポートしているEncodableのタイプは全部このメソッドを実現していますか？
2.SwiftではデフォルトでEncodableをサポートしているメソッドはなんでしょうか？
3.Encoderは何ですか？

質問1の答えはイエスです。Codable.swift.gybファイルにデフォルトタイプの実現があります。
例えば、Intのデフォルト実現は：

```swift:qiita.swift
extension Int : Codable {
  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(self)
  }
}
```

Arrayのデフォルト実現は：

```swift:qiita.swift
extension Array : Encodable where Element : Encodable {
  public func encode(to encoder: Encoder) throws {
    var container = encoder.unkeyedContainer()
    for element in self {
      try container.encode(element)
    }
  }
}
```
なるほど、Swiftでは本当にEncodableのタイプはメソッドを実現しています。

そしたら、Swiftでは、Encodableをサポートしているタイプはどれくらいありますか？
Codable.swift.gybファイルに下記のコードがあります。

```swift:qiita.swift
%{
codable_types = ['Bool', 'String', 'Double', 'Float',
                 'Int', 'Int8', 'Int16', 'Int32', 'Int64',
                 'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64']
}%
```
上記以外、ファイルの一番下に、Array / Set / Dictionary / Optionalなどもあります。

ここまで、質問3だけ残ってます。
encodeメソッドのEncoderはなんでしょうかね。ブラックボックスとして考えれば、結果的にSwiftのオブジェクトをJSON文字列にエンコディングされています。

#JSONEncoder
JSONEncodeの定義：

+ JSONエンコーディング結果の出力フォマット（public struct OutputFormatting）
+ Dateタイプのエンコーディング方式（public enum DateEncodingStrategy）
+ Dataタイプのエンコーディング方式（public enum DataEncodingStrategy）
+ 例外的な浮動小数点エンコーディング方式（public enum NonConformingFloatEncodingStrategy）
+ JSONのkeyのエンコーディング方式（public enum KeyEncodingStrategy）  

続きましては、JSONEncoderを使用してエンコーディングする時、デフォルト使用するプロパティ：

```swift:qiita.swift
open class JSONEncoder {
  open var outputFormatting: OutputFormatting = []

  open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate

  open var dataEncodingStrategy: DataEncodingStrategy = .base64

  open var nonConformingFloatEncodingStrategy:
    NonConformingFloatEncodingStrategy = .throw

  open var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys
}
```
デフォルトのプロパティを便利に使う為、内部タイプの_Optionsと内部プロパティのoptionsも定義されています。

```swift:qiita.swift
open class JSONEncoder {
  fileprivate struct _Options {
    let dateEncodingStrategy: DateEncodingStrategy
    let dataEncodingStrategy: DataEncodingStrategy
    let nonConformingFloatEncodingStrategy:
      NonConformingFloatEncodingStrategy
    let keyEncodingStrategy: KeyEncodingStrategy
    let userInfo: [CodingUserInfoKey : Any]
  }

  fileprivate var options: _Options {
    return _Options(dateEncodingStrategy: dateEncodingStrategy,
      dataEncodingStrategy: dataEncodingStrategy,
      nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy,
      keyEncodingStrategy: keyEncodingStrategy,
      userInfo: userInfo)
  }
}
```
そして、JSONEncoderのコンストラクタ(only one)：

```swift:qiita.swift
open class JSONEncoder {
  public init() {}
}
```
最後は、我々が使用するencodeメソッド：

```swift:qiita.swift
open func encode<T : Encodable>(_ value: T) throws -> Data
```

# __JSONEncoder
これから、JSONEncoder.encodeメソッドの実現から見てみましょう。関数定義は[ここ](https://github.com/apple/swift/blob/master/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L252)です。
実行ロジック：

```swift:qiita.swift
open func encode<T : Encodable>(_ value: T) throws -> Data {
  let encoder = __JSONEncoder(options: self.options)

  guard let topLevel = try encoder.box_(value) else {
    /// throw exception
  }

  /// Handle invalid box value.

  let writingOptions =
    JSONSerialization.WritingOptions(
      rawValue: self.outputFormatting.rawValue)
  do {
     return try JSONSerialization.data(
      withJSONObject: topLevel, options: writingOptions)
  } catch {
    /// throw exception
  }
}
```
encodeの実現：

+ まず、Encoderタイプの__JSONEncoderオブジェクトを作る
+ そして、box_メソッドでパラメーターvalueをJSONエンコーディングできるデーターに変換する
+ エラーチェックなどされたら、FoundationのJSONSerialization.dataを呼び出し、データーのエンコーディングできて、エンコーディング結果のDataオブジェクトをreturn