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の問題は誰もが理解しています。2文字と3文字のクラスのため、名前の競合がしばしば発生します。 Swiftは、明白で理解可能な名前空間を導入することにより、この問題を解決します。 動作しませんが、リリースのために、誰もが修正する必要があります。
- 汎用クラスと関数。 C ++で書いた人にとっては、これはかなり明白なことですが、主にObjective-Cに出くわした人にとっては、これはかなり興味深い新機能であり、使用するのが面白いでしょう。
- 名前付き/デフォルトのパラメーター。 名前付きパラメーターを使用している人は誰も驚かないでしょう;それらは既にObjective-Cにありました。 ただし、デフォルトの設定は非常に便利です。 メソッドが5つの引数を受け入れ、そのうち3つがデフォルトで設定されている場合、関数呼び出しははるかに短くなります。
- 機能はファーストクラスの市民です。 Swiftの関数は一次オブジェクトです。 これは、それらがパラメーターとして他のメソッドに渡され、他のメソッドから返されることを意味します。
- オプションのタイプ。 オプション型は、関数型プログラミングから少し修正された形で私たちにもたらされた興味深い概念です。
最後の機能をさらに詳しく考えてみましょう。 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にないものについて話しましょう。 特定の何かが欠けているとは言えません 本番環境ではまだ使用していません。 しかし、多くの人が不満を言うものがあります。
- プリプロセッサ。 プリプロセッサがない場合、多くのコードを生成するクールなマクロがないことは明らかです。 クロスプラットフォーム開発も困難です。
- 例外。 実行のメカニズムは完全に存在しませんが、NSExceptionをスローすることができ、Objective-Cランタイムがこれをすべて処理します。
- アクセス制御。 Swiftに関する本を読んだ後、多くの人がアクセス修飾子の欠如に混乱しました。 Objective-Cではこれはそうではなく、誰もがそれが必要であると理解し、新しい言語で待っていました。 実際、開発者にはベータアクセス修飾子を実装する時間がありませんでした。 彼らはすでに最終リリースにあります。
- KVO、KVC。 明らかな理由により、キー値の監視とキー値のコーディングはありません。 Swiftは静的言語であり、これらは動的言語の機能です。
- コンパイラー属性。 非推奨のメソッドや特定のプラットフォームにメソッドがあるかどうかを報告するコンパイラー指示はありません。
-
performSelector.
Swiftのこのメソッドは完全に刈り込まれています。 これはかなり安全ではないため、Objective-Cでも注意して使用する必要があります。
それでは、Objective-CとSwiftに干渉する方法について説明しましょう。 SwiftからObjective-Cコードを呼び出すことができることは誰もがすでに知っています。 逆の方向では、すべてがまったく同じように機能しますが、いくつかの制限があります。 列挙、タプル、一般化された型は機能しません。 ポインターはありませんが、CoreFoundation型は直接呼び出すことができます。 多くの人にとって、Swiftから直接C ++コードを呼び出せないことが障害になっています。 ただし、Objective-Cでラッパーを作成し、既に呼び出すことができます。 さて、Swiftから実装されていないObjective-Cクラスではサブクラス化できないのは当然です。
上で言ったように、いくつかのタイプは交換可能です:
-
NSArray < - > Array;
-
NSDictionary < - > Dictionary
; -
NSNumber - > Int, Double, Float
。
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'
ます。
最後に、一見の価値があるリポジトリについて説明します。
- BDDテストフレームワーク: クイック 。 これは、誰もが欠けていた最初のものです。 フレームワークは積極的に開発されており、新しいマッチャーが絶えず追加されています。
- リアクティブプログラミング: RXSwift これは、Swiftが提供するコンストラクトを使用したReactiveCocoaの再考です。
- モデルマッピング: クラスト 。 Swiftのアナログマントル。 JSONオブジェクトを迅速なオブジェクトにマッピングできます。 開発に役立つ多くの興味深いハックが使用されます。
- 便利な JSON処理: SwiftyJSON これは文字通り200行の非常に小さなライブラリです。 しかし、それは列挙の完全な力を示しています。