Tana Gone
Tana Gone
2 min read

Categories

Tags

カスタム属性・関数ビルダー

要するに@_functionBuilderってのをstruct先頭につければ、@BlockAdderって属性を作ることができる。closureを引数に取る関数の引数にカスタム属性を付与すれば、不思議が起こる。

buildBlockというヘンテコ関数が定義できちゃう訳だ。

// Swift5.1 functionality test, ViewBuilder                                     
// 関数ビルダー、DSL
@_functionBuilder public struct BlockAdder {
    public static func buildBlock(_ a: Int) -> Int {
        return a
    }

    public static func buildBlock(_ a: Int, _ b: Int) -> Int {
        return a + b
    }

    public static func buildBlock(_ a: Int, _ b: Int, _ c: Int) -> Int {
        return a + b + c
    }

    public static func buildBlock(_ a: Int, _ b: Int, _ c: Int, _ d: Int) -> Int
 {
        return a + b + c + d
    }
}
func blockAdd(@BlockAdder block: () -> Int) -> Int {
    return block()
}

let one = blockAdd {
    1
} // 1
let three = blockAdd {
    1 // SwiftUIのサンプルではTextに対応するところ
    2
} // 3 (1 + 2)
let six = blockAdd { 1; 2; 3 }    // 6
let ten = blockAdd { 1; 2; 3; 4 } // 10 (1 + 2 + 3 + 4)

print(one)
print(three)
print(six)
print(ten)
print(blockAdd { 1; 4 })

SwiftUIの魔法を実現する仕組み (Custom Attributes, Function Builder)

カスタム属性・Property Wrapper

今度のカスタム属性は、構造体定義の先頭に付与する。すると構造体のプロパティーを監視する事ができる。


@propertyWrapper
struct AlterableOnce<T> { // インスタンス生成後、一回だけ初期化可能な変数。
  private var value: T
  private var setFlag = false
  init(wrappedValue v: T) { // counterを初期化するメソッド
    value = v
  }

  var wrappedValue: T {
    get { value }
    set {
      if setFlag { print("already set"); return }
        value = newValue
        setFlag = true
    }
  }
}
// @AlterableOnce<Int>(wrappedValue: 10) Global変数へは不可
var counter: Int
counter = 20
print(counter)

struct A {
  @AlterableOnce(wrappedValue: 1) //Makeshift属性付き変数counterはset時にprintし...
  var counter: Int
}
var a = A()
print(a.counter)
a.counter = 2
print(a.counter)
a.counter = 3
print(a.counter)
// 20
// 1
// 2
// already set
// 2

こいつの出典は、詳解Swift第5版、Chapter15

プロパティーではなく、Global変数にカスタム属性を付与したら、現時点では未サポートと警告。将来サポートなのか?

someキーワード

メソッド戻値をGenericsにしたい場合、型パラメータ付きプロトコルにしたい場合に-> some Pとする。

以下のコードはTerminal.Appでは動かず、Playgroundで可能。Bug?

protocol Animal {
    func say()
}
    
class Dog: Animal {
    func say() {
        print("bark bark")
    }
}
    
class Bird: Animal {
    func say() {
        print("sing sing")
    }
}

// ジェネリクス関数、引数に型パラメーターを記述できるが、戻り値には出来ない。
func say<A: Animal>(_ animal: A) {
    animal.say()
}

// 関数戻値にAnimal準拠の何かを返したい
// func makeAnimal<A: Animal>() ->  A {
//   if xxx then
//     return Dog()
//   else
//     return Cat()
// }

say(Dog()) // bark bark

// makeAnimal().say() // bark bark
func makeAnimal1() -> some Animal {
    return Dog()
}

func makeAnimal2() -> some Animal {
    return Bird()
}

makeAnimal1().say() // bark bark
makeAnimal2().say() // sing sing

Swift 5.1 に導入される Opaque Result Type とは何か

そして、不透明戻値型とはsome付きのProtocol型の事。

identifiableプロトコルって?

以下のコードも残念ながらTerminal.Appで動作せず。

struct User: Identifiable {
    var id: UUID = UUID()
    var firstName: String
    var lastName: String
}
let a = User(firstName: "OK", lastName: "NG")
print(a.id)

プロパティーidはIntでもコンパイル可能。どういう事?