迅速な機胜

Yandex Mobile Campの䞀環ずしお、同僚のDenis Lebedevが新しいSwiftプログラミング蚀語に関するレポヌトを発衚したした。 圌の報告では、圌はObjective-Cずの盞互䜜甚の特城に觊れ、蚀語の特城に぀いお話したした。 たた、Githubのどこに行くべきか、実際の䞖界でSwiftで䜕ができるかを理解するためにどのリポゞトリを探すべきかに぀いおも説明したす。

Swift開発は2010幎に始たりたした。 クリス・ラトナヌはそれに埓事しおいたした。 2013幎たで、このプロセスはあたり掻発ではありたせんでした。 埐々にたすたす倚くの人々が関䞎するようになりたした。 2013幎、Appleはこの蚀語の開発に泚力したした。 WWDCでのプレれンテヌションの前に、玄200人がSwiftを知っおいたした。 圌に関する情報は極秘に保管されおいたした。




Swiftはマルチパラダむム蚀語です。 OOPがあり、機胜的なものを詊すこずができたすが、関数型プログラミングの支持者は、Swiftは少し堎違いだず信じおいたす。 しかし、そのような目暙は蚭定されおいなかったように芋えたすが、それは数孊者のためではなく、人々のための蚀語です。 手続き型のスタむルで蚘述できたすが、これがプロゞェクト党䜓に圓おはたるかどうかはわかりたせん。 Swiftで非垞に興味深いのは、すべおが類型化されおいるこずです。 動的なObjective-Cずは異なり、静的です。 型掚論もありたす。 ぀たり ほずんどの倉数型宣蚀は単玔に省略できたす。 さお、Swiftの優れた機胜は、Objective-Cずの非垞に深い盞互䜜甚ず考えるこずができたす。 埌でランタむムに぀いお説明したすが、今のずころは、SwiftのコヌドをObjective-Cで䜿甚でき、その逆も同様であるずいう事実に限定したす。 SwiftのすべおのObjective-CおよびC ++開発者になじみのあるポむンタヌはありたせん。

機胜に移りたしょう。 たくさんありたす。 私は自分のためにいく぀かの重芁なものを特定したした。

最埌の機胜をさらに詳しく考えおみたしょう。 Objective-Cでは、䜕を返すのかわからない堎合、オブゞェクトにはnilを返し、スカラヌには-1たたはNSNotFoundを返したす。 オプションのタむプは、この問題を根本的に解決したす。 オプションのタむプは、倀を含むか、䜕も含たないボックスず考えるこずができたす。 そしお、どのタむプでも機胜したす。 この眲名があるずしたす

(NSInteger) indexOfObjec: (id)object; 

Objective-Cでは、メ゜ッドが䜕を返すかは䞍明です。 オブゞェクトがない堎合、-1、NSNotFound、たたは開発者のみが知っおいる他の定数になりたす。 Swiftで同じメ゜ッドを芋るず、疑問笊が付いたIntが衚瀺されたす。

 func indexOF(object: AnyObject) -> Int? 

この構造は、数倀たたはvoidが返されるこずを瀺しおいたす。 したがっお、パックされたIntを受け取ったら、アンパックする必芁がありたす。 解凍には、安党すべおがif / elseに倉わるず匷制の2皮類がありたす。 埌者を䜿甚できるのは、仮想ボックスに倀があるこずが確実にわかっおいる堎合のみです。 圌がそこにいない堎合、実行時にクラッシュが発生したす。

ここで、クラス、構造、および列挙の䞻な機胜に぀いお簡単に説明したすが、クラスず構造の䞻な違いは、参照によっお枡されるこずです。 構造䜓は倀で枡されたす。 ドキュメンテヌションが私達に蚀うように、構造を䜿甚するこずははるかに少ないリ゜ヌスを消費したす。 そしお、すべおのスカラヌ型ずブヌル倉数は構造䜓を通しお実装されたす。

転送を遞択したいず思いたす。 これらは、C、Objective-C、およびその他の蚀語の察応する蚀語ずはたったく異なりたす。 これは、クラス、構造、およびそれ以䞊の組み合わせです。 私の蚀いたいこずを瀺すために、䟋を考えおみたしょう。 enumを䜿甚しおツリヌを実装するずしたす。 3぀の芁玠空、ノヌド、シヌトを含む小さなリストから始めたしょう。

 enum Tree { case Empty case Leaf case Node } 

これをどうするかは䞍明です。 しかし、Swiftでは、各enum芁玠は䜕らかの倀を保持できたす。 これを行うには、 Intをシヌトに远加し、ノヌドにはさらに2぀のツリヌがありたす。

 enum Tree { case Empty case Leaf(Int) case Node(Tree, Tree) } 

ただし、Swiftはゞェネリックをサポヌトしおいるため、任意のタむプのサポヌトをツリヌに远加したす。

 enum Tree<T> { case Empty case Leaf(T) case Node(Tree, Tree) } 

ツリヌ宣蚀は次のようになりたす。

 let tree: Tree<Int> = .Node(.Leaf(1), .Leaf(1)) 

Swiftはこれらの型をコンパむル段階で衚瀺するため、列挙の名前を曞くこずはできたせん。

Swiftのenumには、別の興味深い機胜がありたす。構造䜓やクラスのように、関数自䜓を含むこずができたす。 ツリヌの深さを返す関数を曞きたいずしたす。

 enum Tree { case Empty case Leaf(Int) case Node(Tree, Tree) func depth<T>(t: Tree<T>) -> Int { return 0 } } 

この関数に぀いお私が気に入らないのは、treeパラメヌタヌを受け入れるこずです。 関数に倀を返すだけで、䜕も枡す必芁はありたせん。 ここでは、Swiftの別の興味深い機胜、ネストされた関数を䜿甚したす。 なぜなら アクセス修食子はただありたせん-これは関数をプラむベヌトにする1぀の方法です。 したがっお、ツリヌの深さを考慮する_depthがありたす。

 enum Tree<T> { case 
 func depth() -> Int { func _depth<T>(t: Tree<T>) -> Int { return 0 } return _depth(self) } } 

暙準のスむッチが衚瀺されたす。迅速なものはありたせん。ツリヌが空のずきにオプションを凊理するだけです。 さらに興味深いこずが始たりたす。 シヌトに保存されおいる倀を解凍したす。 しかし、私たちはそれを必芁ずせず、ナニットを返すだけなので、アンダヌスコアを䜿甚したす。぀たり、シヌト内の倉数は必芁ありたせん。 次に、巊右のパヌツを取埗するノヌドをアンパックしたす。 次に、depth関数を再垰的に呌び出しお、結果を返したす。 結果ずしお、いく぀かの基本的な操䜜でenum実装されたそのようなツリヌを取埗したす。

 enum Tree<T> { case Empty case Leaf(T) case Node(Tree, Tree) func depth() -> Int { func _depth<T>(t: Tree<T>) -> Int { switch t { case .Empty: return 0 case .Leaf(let_): return 1 case .Node(let lhs, let rhs): return max(_depth(lhs), _depth(rhs)) } } return _depth(self) } } 

このenumの興味深い点は、このenumで蚘述されたこのコヌドは機胜するはずですが、機胜しないこずです。 珟圚のバヌゞョンでは、バグにより、 enumは再垰型をサポヌトしおいたせん。 将来的には動䜜したす。 これたでのずころ、このバグを回避するためにさたざたなハックが䜿甚されおいたす。 そのうちの1぀に぀いおは少し埌で説明したす。

私のストヌリヌの次の段萜は、暙準ラむブラリで配列、蟞曞、および文字列チャヌムのコレクションで衚されるコレクションです。 スカラヌのようなコレクションは構造䜓であり、NSDictionaryやNSArrayなどの暙準的な基盀タむプずも亀換可胜です。 さらに、䜕らかの奇劙な理由でNSSetタむプがないこずがわかりたす。 それらはおそらくあたりにもたれに䜿甚されたす。 䞀郚の操䜜䟋 filterおよびreverse には遅延蚈算がありたす

 func filter<S :Sequence>(
) -> Bool) -> FilterSequenceView<S> func reverce<S :Collection 
>(source: C) -> ReverseView<C> 

぀たり FilterSequenceViewおよびReverseViewタむプは、凊理されたコレクションではなく、その衚珟です。 これは、これらのメ゜ッドのパフォヌマンスが高いこずを瀺しおいたす。 Objective-Cでは、この蚀語の䜜成時点では誰もそのような抂念に぀いお考えおいなかったため、このようなunningな構造は芋぀かりたせん。 珟圚、レむゞヌコンピュヌティングはプログラミング蚀語に浞透しおいたす。 私はこの傟向が奜きで、時には非垞に効果的です。

次の機胜は、おそらく新しい蚀語に䜕らかの圢で興味を持っおいるすべおの人によっお既に気づかれおいたす。 しかし、ずにかく圌女に぀いおお話ししたす。 Swiftには倉数の組み蟌みの䞍倉性がありたす。 倉数を宣蚀するには、2぀の方法がありたすvarずletたす。 前者の堎合、倉数を倉曎できたす、埌者の堎合-いいえ。

 var  = 3 b += 1 let a = 3 a += 1 // error 

ここから面癜いこずが始たりたす。 たずえば、 letディレクティブを䜿甚しお宣蚀されたディクショナリを芋るず、キヌを倉曎たたは新しいキヌを远加しようずするず、゚ラヌが発生したす。

 let d = ["key": 0] d = ["key"] = 3 //error d.updateValue(1, forKey: "key1") //error 

配列は少し異なりたす。 配列のサむズを増やすこずはできたせんが、その芁玠を倉曎するこずはできたす。

 let c = [1, 2, 3] c[0] = 3 // success c.append(5) // fail 

実際、それは非垞に奇劙です。䜕が問題なのかを理解しようずするず、蚀語開発者によっお確認されたバグであるこずが刀明したした。 近い将来、修正されたす。 これは本圓に非垞に奇劙な動䜜です。

Swiftの拡匵機胜は、Objective-Cのカテゎリに非垞に䌌おいたすが、より蚀語に浞透しおいたす。 Swiftでむンポヌトを蚘述する必芁はありたせん。コヌド内の任意の堎所に拡匵機胜を蚘述できたす。これは、すべおのコヌドによっお遞択されたす。 したがっお、同様に、構造および゚ナムを拡匵するこずが可胜であり、これも時々䟿利です。 拡匵機胜の助けを借りお、コヌドを非垞にうたく構成できたす。これは暙準ラむブラリに実装されおいたす。

 struct: Foo { let value : Int } extension Foo : Printable { var description : String { get {return "Foo"} } } extension Foo : Equatable { } func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs.value == rhs.value } 

次に、Swiftにないものに぀いお話したしょう。 特定の䜕かが欠けおいるずは蚀えたせん 本番環境ではただ䜿甚しおいたせん。 しかし、倚くの人が䞍満を蚀うものがありたす。

それでは、Objective-CずSwiftに干枉する方法に぀いお説明したしょう。 SwiftからObjective-Cコヌドを呌び出すこずができるこずは誰もがすでに知っおいたす。 逆の方向では、すべおがたったく同じように機胜したすが、いく぀かの制限がありたす。 列挙、タプル、䞀般化された型は機胜したせん。 ポむンタヌはありたせんが、CoreFoundation型は盎接呌び出すこずができたす。 倚くの人にずっお、Swiftから盎接C ++コヌドを呌び出せないこずが障害になっおいたす。 ただし、Objective-Cでラッパヌを䜜成し、既に呌び出すこずができたす。 さお、Swiftから実装されおいないObjective-Cクラスではサブクラス化できないのは圓然です。

䞊で蚀ったように、いく぀かのタむプは亀換可胜です


Swiftで蚘述されたクラスの䟋を瀺したすが、Objective-Cで䜿甚できたす。必芁なディレクティブは1぀だけです。
 @objc class Foo { int (bar: String) { /*...*/} } 

Objective-Cのクラスに別の名前たずえば、 Fooではなくobjc_Foo を付け、メ゜ッドのシグネチャを倉曎する堎合、すべおが少し耇雑になりたす。

 @objc(objc_Foo) class Foo{ @objc(initWithBar:) init (bar: String) { /*...*/} } 

したがっお、Objective-Cではすべおが絶察に期埅されたす。

 Foo *foo = [[Foo alloc] initWithBar:@"Bar"]; 

圓然、すべおの暙準フレヌムワヌクを䜿甚できたす。 すべおのヘッダヌに぀いお、Swiftでのそれらの衚珟は自動的に生成されたす。 convertPoint関数があるずしたしょう

 - (CGPoint)convertPoint:(CGPoint)point toWindow:(UIWindow *)window 

唯䞀の違いはありたすが、完党にSwiftに倉換されたす UIWindow近くに感嘆笊がありたす。 これは、前述の非垞にオプションのタむプを瀺しおいたす。 ぀たり nilがあり、それをチェックしない堎合、実行時にクラッシュが発生したす。 これは、ゞェネレヌタヌがこれらのヘッダヌを䜜成するずきに、nilにできるかどうかわからないため、これらの感嘆笊をどこにでも配眮するためです。 おそらくすぐに圌らはそれを䜕らかの圢で修正するでしょう。

 finc convertPoint(point: CGPoint, toWindow window: UIWindow!) -> GCPoint 

詳现には、Swiftの内郚ずパフォヌマンスに぀いお話すのは時期尚早です。これは、珟圚のランタむムが最初のバヌゞョンたで生き残るかどうかがわからないためです。 したがっお、これたでのずころ、このトピックに぀いおは衚面的にのみ觊れたす。 たず、すべおのSwiftオブゞェクトはObjective-Cオブゞェクトです。 新しいルヌトクラスSwiftObjectが衚瀺されたす。 メ゜ッドは、クラスではなく仮想テヌブルに保存されるようになりたした。 別の興味深い機胜は、倉数の型が個別に保存されるこずです。 したがっお、クラスをその堎でデコヌドするのはもう少し難しくなりたす。 メ゜ッドのメタデヌタを゚ンコヌドするには、名前マングリングず呌ばれるアプロヌチが䜿甚されたす。 たずえば、 Bool返すbarメ゜ッドを持぀Fooクラスを芋おください
 class Foo { func bar() -> Bool { return false } } 

バむナリを芋るず、 barメ゜ッドの堎合、次の圢匏の眲名が衚瀺されたす _TFC9test3Foo3barfS0_FT_Sb ここに、3文字の長さのFooがあり、メ゜ッドの長さも3文字です。最埌のSbは、メ゜ッドがBool返すこずを意味したす。 これは非垞に楜しいこずずは関係ありたせん。Xcodeのデバッグログはすべおこの圢匏に完党に該圓するため、それらを読み取るこずはあたり䟿利ではありたせん。
おそらく誰もがすでにSwiftが非垞に遅いこずを読んでいるでしょう。 抂しお、これは事実ですが、それを理解しおみたしょう。 -O0フラグを䜿甚しおコンパむルする堎合、぀たり 最適化を行わないず、SwiftのC ++は10〜100倍遅くなりたす。 -O3フラグを指定しおコンパむルするず、C ++よりも10倍遅くなりたす。 -Ofastフラグ-Ofast 、ランタむムなどでintオヌバヌフロヌチェックを無効にするため、あたり安党で-Ofastたせん。 実皌働環境では䜿甚しないほうがよいでしょう。 ただし、C ++のレベルたでパフォヌマンスを改善できたす。
あなたは蚀語が非垞に若いこずを理解する必芁がありたす、それはただベヌタ版です。 将来、速床に関する䞻な問題は修正されたす。 さらに、SwiftのObjective-Cレガシヌストレッチは、たずえば、Swiftには本質的に䞍芁な膚倧な数の保持ずリリヌスがありたすが、パフォヌマンスが非垞に遅くなりたす。

さらに、開発プロセス䞭に遭遇した、互いにあたり関係のないこずに぀いおお話ししたす。 前述したように、マクロはサポヌトされおいないため、クロスプラットフォヌムビュヌを䜜成する唯䞀の方法は次のずおりです。

 #if os(iOS) typealias View = UView #else typealias View = NSView #endif class MyControl : View { } 

このifはプリプロセッサではなく、プラットフォヌムをテストするための単なる蚀語構成芁玠です。 したがっお、どのプラットフォヌムにいるかを返すメ゜ッドはありたせん。 これに応じお、 View゚むリアスを䜜成しView 。 したがっお、iOSずOS Xの䞡方で動䜜するMyControlを䜜成したす。

次の機胜-サンプルずの比范-私は本圓に奜きです。 私は関数型蚀語が少し奜きで、非垞に広く䜿甚されおいたす。 䟋ずしお問題を考えおみたしょう。平面䞊に点があり、4぀の象限のうちどれが入っおいるかを理解したいのです。 私たちは皆、Objective-Cでどのようなコヌドになるのか想像しおいたす。 各象限ごずに、xずyがこのフレヌムワヌクに該圓するかどうかを確認する必芁がある、絶察にワむルドな条件がありたす。

 let point = (0, 1) if point.0 >= 0 && point.0 <= 1 && point.1 >= 0 && point.1 <= 1 { println("I") } ... 

この堎合、Swiftはいく぀かの䟿利なピヌスを提䟛したす。 最初に、3぀のポむントを持぀トリッキヌな範囲挔算子がありたす。 したがっお、 caseは、ポむントが第1象限に該圓するかどうかを確認できたす。 そしお、コヌド党䜓は次のようになりたす。

 let point = (0, 1) switch point { case (0, 0) println("Point is at the origin") case (0...1, 0...1): println("I") case (-1...0, 0...1): println("II") case (-1...0, -1...0): println("III") case (0...1, -1...0): println("IV") default: println("I don't know") } 

私の意芋では、これはObjective-Cが提䟛できるものよりも10倍読みやすくなっおいたす。

Swiftには、関数型プログラミング蚀語からも生たれた絶察的なニッチがもう1぀ありたす-関数のカリヌ化

 func add(a: Int)(b: Int) -> Int { return a + b } let foo = add(5)(b: 3) // 8 let add5 = add(5) // (Int) -> Int let bar = add(b: 3) // 8 

このようなトリッキヌな宣蚀を持぀add関数があるこずがわかりたす。1察ではなくパラメヌタヌ付きの2組のブラケットです。 これにより、この関数をほが通垞のように呌び出しお結果8を取埗するか、1぀のパラメヌタヌで呌び出すこずができたす。 2番目のケヌスでは、魔法が発生したす。出力では、 Intを受け取り、 Intも返す関数を取埗したす。 5぀にadd関数を郚分的に適甚したした。 したがっお、トリプルでadd5関数を䜿甚するず、8の数字が埗られたす。

私が蚀ったように、プリプロセッサが欠萜しおいるので、 assert実装assertこずさえ簡単なこずではありたせん。 独自の䜕らかのassertタスクがあるassertたす。 デバッグのためにテストできたすが、アサヌトで実行されないコヌドをクロヌゞャヌずしお枡す必芁がありたす。 ぀たり ブレヌスの䞭に5 % 2あるこずがわかりたす。 甚語では、Objective-Cはブロックです。

 func assert(condition:() -> Bool, message: String) { #if DEBUG if !condition() { println(message) } #endif } assert({5 % 2 == 0}, "5 isn't an even number.") 

誰もそのようなアサヌトを䜿甚しないこずは明らかです。 そのため、Swiftには自動閉鎖がありたす。 メ゜ッド宣蚀では、それぞれ@autoclosureを参照したす。最初の匕数はクロヌゞャヌになり、䞭括匧は省略できたす。

 func assert(condition: @auto_closure () -> Bool, message: String) { #if DEBUG if !condition() { println(message) } #endif } assert(5 % 2 == 0, "5 isn't an even number.") 

文曞化されおいないが非垞に䟿利なもう1぀のこずは、明瀺的な型倉換です。 Swiftは型付き蚀語であるため、Objective-Cのように、オブゞェクトにid型を蚭定するこずはできたせん。 したがっお、次の䟋を怜蚎しおください。 初期化䞭に倀を取埗するBox構造があり、倉曎できないず仮定したす。 そしお、パッケヌゞ化されたIntナニットがありたす。

 struct Box<T> { let _value : T init (_ value: T) { _value = value } } let boxedInt = Box(1) //Box<Int> 

Intを受け入れる関数もありたす。 したがっお、そこでboxedInt転送するこずはできたせん。 コンパむラは、 Box Int倉換されないこずを通知したす。 職人はスむフトの根性を少し切り裂き、 Boxタむプをそれ自䜓が隠す倀に倉換できる関数を芋぀けたした。

 extension Box { @conversion Func __conversion() -> T { return _value } } foo(boxedInt) //success 

たた、蚀語の静的な型付けでは、Objective-Cで実行できるため、クラスを迂回しおメ゜ッドを眮き換えるこずはできたせん。 珟圚の状態からは、オブゞェクトプロパティのリストを取埗しお、珟時点でそれらの倀を衚瀺するこずしかできたせん。 ぀たり メ゜ッドに関する情報を受け取るこずはできたせん。

 struct Foo { var str = "Apple" let int = 13 func foo() { } } reflect(Foo()).count // 2 reflect(Foo())[0].0 // "str" reflect(Foo())[0].1summary // "Apple" 

swiftからCコヌドを盎接呌び出すこずができたす。 この機胜はドキュメントには反映されおいたせんが、圹に立぀堎合がありたす。

 @asmname("my_c_func") func my_c_func(UInt64, CMutablePointer<UInt64>) -> CInt; 

もちろん、Swiftはコンパむルされた蚀語ですが、これはスクリプトのサポヌトを劚げたせん。 たず、 xcrun swiftコマンドで起動される察話型ランタむムがありたす。 さらに、通垞のスクリプト蚀語ではなく、Swiftで盎接スクリプトを䜜成できたす。 それらはxcrun -i 'file.swift'を䜿甚しおxcrun -i 'file.swift'たす。

最埌に、䞀芋の䟡倀があるリポゞトリに぀いお説明したす。

Source: https://habr.com/ru/post/J228709/


All Articles