テーマ 第一部: モダンなプログラミング言語としてのSwift Swiftの言語仕様やコンパイラなどについて語ります。 キーワード Swift 4.1 SwiftとObjective-C Swiftと他の言語 マルチプラットフォーム Conditional conformance Memory ownership 第二部: iOSアプリの開発言語としてのSwift iOSアプリを開発するにあたって、よりSwiftyな手法やAppleのフレームワークとの上手な付き合い方などについて語ります。 キーワード UIKitとIUO より安全にUIKitを扱う方法
struct Box<T> { let value: T } extension Box: Equatable where T: Equatable { static func == (lhs: Box, rhs: Box) -> Bool { return lhs.value == rhs.value } }
func ==<X>(a: [X], b: [X]) where X: Equatable { ... }
(edited)//:configuration = Release OTHER_SWIFT_FLAGS = -enable-experimental-conditional-conformances //:completeSettings = some OTHER_SWIFT_FLAGS
func ==
いらない?// Will not currently compile protocol Sequence { associatedtype SubSequence: Sequence where Iterator.Element == SubSequence.Iterator.Element, SubSequence.SubSequence == SubSequence // Returns a subsequence containing all but the first 'n' items // in the original sequence. func dropFirst(_ n: Int) -> Self.SubSequence // ... }
// Test for a simulator destination #if (arch(i386) || arch(x86_64)) && (!os(macOS)) print("Simulator") #else print("Device") #endif // More restrictive test for iOS simulator // Adjust the os test for watchOS, tvOS #if (arch(i386) || arch(x86_64)) && os(iOS) // iOS simulator code #endif
protocol
で init
を書くと self
を上書きできるcoder aDecoder: NSCoder
を実装すればstatic func make() -> Self
を実装すれば良いのではinit(withNibName: )
にnilをつっこむと、その<クラス名>.nibから生成できるprotocol Factoryable { init(factory: () -> Self) } extension Factoryable { init(factory: () -> Self) { self = factory() } } class MyAnimal: Factoryable { convenience init(name: String) { if name.contains("Cat") { self.init { MyCat() } } else { self.init { MyDog() } } } } class MyCat: MyAnimal { var meou: String { return "meou" } } class MyDog: MyAnimal { } let x = MyCat(name: "Dog") x.meou // Crash!!
import Foundation protocol Factoryable { init(factory: () -> Self) } extension Factoryable { init(factory: () -> Self) { self = factory() } } class MyAnimal: Factoryable { convenience init(name: String) { if name.contains("Cat") { self.init { MyCat() } } else { self.init { MyDog() } } } } class MyCat: MyAnimal { var meou: String { return "meou" } } class MyDog: MyAnimal { func bark(arg: String) -> String { return arg } } let x = MyCat(name: "Dog") let y = x.meou // Crash!!
rintaro - 2017/05/19 https://gist.github.com/rintaro/b16f05411cfe540754285dcc0da60174 Gist factory_init.swift これも Dog(type: "cat") 呼べちゃうっていう問題がきつい
この辺で話題になった// RUN: %target-typecheck-verify-swift -swift-version 4
dynamic_self_swift4
// Semi-bogus factory init pattern
static func init
の話もしてたけど、それは少なくともまだ戻り値の型がちゃんと正しい型でしたな、こいつ Dog
なのにデバッグ情報としての型は Cat
(edited)init(factory: () -> Self)
のwitness-tableには、Cat,Dogはなくて、Animalだけがある、だから、convenience initはCat/Dogからは呼べない、が正しいと思うんですよねself = factory()
が書けるのが問題な気が…通常のclassの convenience init
でも self = ...
はエラーになるはずprotocol SomeProtocol: nonclass
くる?class Animal { factory init(name: String) { ... } } class Cat: Animal { init() { super.init(name: "Cat") } } // Compile Error Cat.init(name: "Cat") // Compile Error
init(throws x: String) throws { // この中でOptionalなsuper.init(convenienceでself.init)を呼んでnilならErrorを出したい }
これめちゃくちゃ困ってるんですが、classだけで完結できる方法ありますかね?struct Box<T> { let value: T } extension Box: Equatable where T: Equatable { static func == (lhs: Box, rhs: Box) -> Bool { return lhs.value == rhs.value } }
これが普通に動いてる気がするdictionaryRepresentation
してからファイルにしたらいいんじゃないかな。protocol Foo { associatedtype Bar func foo<T: HasB>(t: T) where T.B == Bar } protocol HasB { associatedtype B } struct C<E>: Foo { typealias Bar = [E] }
この状態でCにfooを補完させると func foo<T>(t: T) where T : HasB, [E] == T.B { }
というコンパイルの通らないコードが補完されるCurPtr = BufferEnd
確かにこれいきなり出てきたらギョッとする{ ∃X where X: P, X }
より { ∃X ∈ P, X }
のほうがいいかもなって思ったbody
が T
を受けてくれたらいいのに・・・。self.disposeBag = nil // その他処理... }
みたいなときに、 deinit
の発行が その他処理の「後」になることがあり得るか?という話に関しては、無いかなーと思っていますが、検証はしていません。 (edited)protocol P {} protocol Foo { var foo: Int { get } } extension Int: P {} extension Array: Foo where Element: P { var foo: Int { return count } } let a = [1,2,3] print(a is Foo)
(edited)main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^ warning: Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array<Swift.Int>': 'main.Foo') Could not cast value of type 'Swift.Array<Swift.Int>' (0x7f6f03a20240) to 'main.Foo' (0x7f6f03a20280). #0 0x0000000003f24d64 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x3f24d64) #1 0x0000000003f250a6 SignalHandler(int) (/usr/bin/swift+0x3f250a6) #2 0x00007f6f07f2c390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #3 0x00007f6f0666b428 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x35428) #4 0x00007f6f0666d02a abort (/lib/x86_64-linux-gnu/libc.so.6+0x3702a) #5 0x00007f6f038fbf13 (/usr/lib/swift/linux/libswiftCore.so+0x39df13) #6 0x00007f6f038f84d9 (/usr/lib/swift/linux/libswiftCore.so+0x39a4d9) #7 0x00007f6f038f852b (/usr/lib/swift/linux/libswiftCore.so+0x39a52b) #8 0x00007f6f038f9a78 _dynamicCastToExistential(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetExistentialTypeMetadata<swift::InProcess> const*, swift::DynamicCastFlags) (/usr/lib/swift/linux/libswiftCore.so+0x39ba78) #9 0x00007f6f0835b2ad #10 0x00007f6f0835b10e #11 0x0000000000fed1ce llvm::MCJIT::runFunction(llvm::Function*, llvm::ArrayRef<llvm::GenericValue>) (/usr/bin/swift+0xfed1ce) #12 0x0000000000ff1692 llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, char const* const*) (/usr/bin/swift+0xff1692) #13 0x00000000004d9076 swift::RunImmediately(swift::CompilerInstance&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, swift::IRGenOptions&, swift::SILOptions const&
(edited)true
stderr:main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^
protocol P {} protocol Foo { var foo: Int { get } } extension Int: P {} extension Array: Foo where Element: P { var foo: Int { return count } } let a = [1,2,3] print(a is Foo)
protocol P {} protocol Foo { var foo: Int { get } } extension Int: P {} extension Array: Foo where Element: P { var foo: Int { return count } } let a = [1,2,3] print(a is Foo)
swift-4.0-RELEASE
/usercode/main.swift:7:1: error: extension of type 'Array' with constraints cannot have an inheritance clause extension Array: Foo where Element: P { ^ ~~~
swift-4.0.2-RELEASE
/usercode/main.swift:7:1: error: extension of type 'Array' with constraints cannot have an inheritance clause extension Array: Foo where Element: P { ^ ~~~
swift-4.0.3-RELEASE
/usercode/main.swift:7:1: error: extension of type 'Array' with constraints cannot have an inheritance clause extension Array: Foo where Element: P { ^ ~~~
swift-4.1-RELEASE
/usercode/main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^ warning: Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array<Swift.Int>': 'main.Foo') Could not cast value of type 'Swift.Array<Swift.Int>' (0x7fc841935240) to 'main.Foo' (0x7fc841935280). /usr/bin/swift[0x3f24d54] /usr/bin/swift[0x3f25096] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7fc845641390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7fc843d80428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7fc843d8202a] /usr/lib/swift/linux/libswiftCore.so(+0x39df13)[0x7fc841810f13] /usr/lib/swift/linux/libswiftCore.so(+0x39a4d9)[0x7fc84180d4d9] /usr/lib/swift/linux/libswiftCore.so(+0x39a52b)[0x7fc84180d52b] /usr/lib/swift/linux/libswiftCore.so(+0x39ba78)[0x7fc84180ea78] [0x7fc845a702ad] [0x7fc845a7010e] /usr/bin/swift[0xfed1ce] /usr/bin/swift[0xff1692] /usr/bin/swift[0x4d9076] /usr/bin/swift[0x4c35d3] /usr/bin/swift[0x4beecc] /usr/bin/swift[0x4778c4] ...
swift-4.1.1-RELEASE
/usercode/main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^ warning: Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array<Swift.Int>': 'main.Foo') Could not cast value of type 'Swift.Array<Swift.Int>' (0x7f2e73ecc240) to 'main.Foo' (0x7f2e73ecc280). /usr/bin/swift[0x3f24d64] /usr/bin/swift[0x3f250a6] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f2e77bd8390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7f2e76317428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7f2e7631902a] /usr/lib/swift/linux/libswiftCore.so(+0x39df13)[0x7f2e73da7f13] /usr/lib/swift/linux/libswiftCore.so(+0x39a4d9)[0x7f2e73da44d9] /usr/lib/swift/linux/libswiftCore.so(+0x39a52b)[0x7f2e73da452b] /usr/lib/swift/linux/libswiftCore.so(+0x39ba78)[0x7f2e73da5a78] [0x7f2e780072ad] [0x7f2e7800710e] /usr/bin/swift[0xfed1ce] /usr/bin/swift[0xff1692] /usr/bin/swift[0x4d9076] /usr/bin/swift[0x4c35d3] /usr/bin/swift[0x4beecc] /usr/bin/swift[0x4778c4] ...
swift-4.2-DEVELOPMENT-SNAPSHOT-2018-05-02-a
true
/usercode/main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^
protocol P {} protocol Foo { var foo: Int { get } } extension Int: P {} extension Array: Foo where Element: P { var foo: Int { return count } } let a = [1,2,3] print(a is Foo)
swift-4.2-DEVELOPMENT-SNAPSHOT-2018-05-02-a
true
/usercode/main.swift:12:9: warning: 'is' test is always true print(a is Foo) ^
where Key == Value
やばそうprotocol A { static func foo() } extension Dictionary: A where Key == Value { static func foo() { print(type(of: self)) } } extension Array where Element: A { static func foo() { Element.foo() } } [String: String].foo() // Dictionary<String, String> [[String: String]].foo() // Dictionary<Dictionary<String, String>, Dictionary<String, String>>
protocol A { static func foo() } extension Dictionary: A where Key == Value { static func foo() { print(type(of: self)) } } extension Array where Element: A { static func foo() { Element.foo() } } [String: String].foo() // Dictionary<String, String> [[String: String]].foo() // Dictionary<Dictionary<String, String>, Dictionary<String, String>>
swift-4.2-DEVELOPMENT-SNAPSHOT-2018-05-02-a
Dictionary<String, String>.Type Dictionary<String, String>.Type
protocol A {} protocol B: A {} protocol C: A {} protocol D: B, C {} struct X<T>: A{} extension X: D where T == Int { // Type 'X<T>' does not conform to protocol 'B' }
main.swift:11:1: error: conditional conformance of type 'X<T>' to protocol 'D' does not imply conformance to inherited protocol 'B' extension X: D where T == Int { // Type 'X<T>' does not conform to protocol 'B' ^ main.swift:11:1: error: conditional conformance of type 'X<T>' to protocol 'D' does not imply conformance to inherited protocol 'C' extension X: D where T == Int { // Type 'X<T>' does not conform to protocol 'B' ^ main.swift:11:1: note: did you mean to explicitly state the conformance like 'extension X: C where ...'? extension X: D where T == Int { // Type 'X<T>' does not conform to protocol 'B' ^
import PlaygroundSupport
するのはPlaygroundBookでしかできない?
Linked Frameworks
からaddするのかと思ったらそれもできないし@objc protocol Foo { optional func aaa() }
let a: [String] = [] let b: [Int] = a as! [Int]
(edited)@escaping
/@noescape
はオーバーロードできる?func a(_ f: @escaping () -> ()) { } func a(_ f: () -> ()) { }
func a(_ f: @escaping () -> ()) { } func a(_ f: () -> ()) { }
swift-4.1.1-RELEASE
/usercode/main.swift:2:6: error: invalid redeclaration of 'a' func a(_ f: () -> ()) { } ^ /usercode/main.swift:1:6: note: 'a' previously declared here func a(_ f: @escaping () -> ()) { } ^
@objc
をつけると作れるprotocol A {} class B: A{} class C: A{}
vs enum A { case b(B) case c(C) }
class A { func hoge(a: Sub) -> Sub { return Sub() } } class B: A { override func hoge(a: Super) -> SubSub { return SubSub() } } class Super {} class Sub: Super {} class SubSub: Sub {}
(edited)class A { func hoge(a: Sub) -> Sub { return Sub() } } class B: A { override func hoge(a: Super) -> SubSub { return SubSub() } } class Super {} class Sub: Super {} class SubSub: Sub {}
swift-4.1.1-RELEASE
Any
と String
のときダメなんです??protocol Myself {} extension Myself { var `Self`: Self.Type { return type(of: self) } static var `Self`: Self.Type { return self } } class Foo: Myself { func bar() { Self.hoge() } static func baz() { Self.hoge() } static func hoge() { print("called") } }
今でもこういうことはできると聞きましたenum
vs protocol
をやっていきたいと思います。 protocol A {} class B: A{} class C: A{}
vs enum A { case b(B) case c(C) }
seald
がないので、protocol
を利用した上の例では、予想しないような実装(クラス・構造体)が作られる恐れがあり、またパターンマッチで網羅性を検査することができません。enum
がいいのか、という議論になるかというとそうではなくて、(勘違いだったら申し訳ないんですが)Swiftのenum
の各タグ(?、たとえば上記の例でいうa
とかb
)はたしか型パラメーターが取れないです。protocol
をつかって次のようにやるしかない、といった感じになるんじゃないでしょうか。 public protocol HList { } public struct HNil: HList { init() { } } public struct HCons<H, T: HList>: HList { public let head: H public let tail: T public init(_ h: H, _ t: T) { self.head = h self.tail = t } }
HNil
とHCons
がありますが、HCons
が型パラを取るので、こうしています。enum
だと各タグ(?)は型パラメーターが取れないから、enum
ではできそうにない、という例として使いました。 (edited)enum HList0 { case `nil` } enum HList1<T1> { case `nil` case list0(HList0) } enum HList2<T1, T2> { case `nil` case list1(HList1<T2>) } enum HList3<T1, T2, T3> { case `nil` case list2(HList2<T3, T3>) }
HCons
と HNil
の二つがあればいいんじゃない?public protocol HList { } public struct HNil: HList { init() { } } public struct HCons<H, T: HList>: HList { public let head: H public let tail: T public init(_ h: H, _ t: T) { self.head = h self.tail = t } }
// Kotlin sealed class HList { class HNil: HList() class HCons<H, T: HList>(val head: H, val tail: T): HList() }
head
と tail
にマッチングできないかな?
HCons<Int, HCons<String, HCons<Bool, HNil>>>
とは、先頭の値の型が Int
であり、かつ2番目の値の型が String
であり、そして3番目の値の型が Bool
であることを示している。switch list { case let .Cons(v): ??? case let .Nil: ??? }
みたいな な例は、どんなリストに対しても「どのパターンに行けばいいか決定できない」というケースがないので網羅しています。switch list { case let .Cons(v): ??? case let .Cons(.Cons(v)): ??? }
protocol HListProtocol { associatedtype HeadType associatedtype ConsType: HListProtocol func asEnum() -> EHList<HeadType, ConsType> } indirect enum EHList<H, L: HListProtocol> { case `nil` case cons(H, EHList<L.HeadType, L.ConsType>) }
switch hlist { // たとえばhlistの型は HCons<Int, HCons<String, HCons<Bool, HNil>>> だとする。これはコンパイラなら知ってるぞ case let .HCons(v): ??? // 当然 :point_up: の先頭の型は Int だから、vはIntだぞ case let .HNil: ??? }
HList
ならまあ、どうにかJavaでもできそうなんですけど、HList
の値があったとき、それらをアペンドする関数をかけるか?というのは実はSwiftとかじゃないとキビしいと思います。HCons<Int, HCons<String, HCons<Bool, HNil>>>
とHCons<Int, HCons<String, HCons<Bool, HNil>>>
があったら、アペンドするとHCons<Int, HCons<String, HCons<Bool, HCons<Int, HCons<String, HCons<Bool, HNil>>>>>>
という型が帰ってくる関数です。extension
が型パラメーター取れないんだった!extension Optional { func flatten<T>() -> T? where Wrapped == T? { return flatMap { $0 } } }
extension<T> Optional where Wrapped == T? { func flatten() -> T? { return flatMap { $0 } } }
extension
で型パラ取らせてくれーassosicatedtype
しているので、関数の中というよりは、extension
の中ですぐ必要な感じです。public protocol HAppend { associatedtype A: HList associatedtype B: HList associatedtype C: HList static func append(_ hl1: A, _ hl2: B) -> C } extension<D: HList> HAppend { typealias A = HNil typealias B = D typealias C = D static func append(_ hl1: Nil, _ hl2: D) -> D = hl2 }
(edited)Nat
を作って、それつかってHLIstにアクセスしているけど、同様にこういうのもつくれない……)