突然、モバイル開発の経験がなかったのと同じように、ObjCではなくSwiftを通じて正確にマスターし始めた多くの知人が発見されました。 そのうちの何人かは、私にHabréでの経験を共有するように促しました。
したがって、ここにトップ5の「トラップ」があります。これをタイムリーに理解すると、間違いなく時間を節約できます。
1.ブロック(短絡)によりメモリリークが発生する場合があります
私のように、ObjCをバイパスしてモバイル開発に来たのであれば、おそらくAppleの自動参照カウントドキュメントは最も重要な入力資料の1つでしょう。 実際には、浸漬によって新しい言語を「スピーディ」に学習すると(つまり、実際のプロジェクトをすぐに見始めることで)、「ここに今すぐポップアップウィンドウを表示する」などのタスクに関連しない「理論」をスキップする傾向が生じる場合があります。 ただし、ARCマニュアルには、リークの原因となる非自明なクロージャープロパティを具体的に説明する非常に重要なセクションが含まれています。
だから、「トラップ」の例。 メモリから決して消去されないシンプルなコントローラー:
class ViewController: UIViewController { var theString = "Hello World" var whatToDo: (()->Void)! override func viewDidLoad() { whatToDo = { println(self.theString) } } override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { whatToDo() navigationController!.setViewControllers([], animated: true) } deinit { println("removed from memory") } }
起動し、画面上で指で。 経験がほとんどない場合、コンソールに誤って表示されることが予想されます。
Hello World removed from memory
しかし、実際に私たちは見ます:
Hello World
つまり、私たちはコントローラーに連絡する能力を失い、彼は記憶にとどまり続けました。
なぜそうですか? この無邪気な行で自己を呼び出すことが判明
{ println(self.theString) }
whatToDoループバックから厳密なコントローラーリンクを自動的に作成します。 コントローラー自体がwhatToDoを厳密に参照するため、結果として、メモリー内で互いに厳密に参照する2つのオブジェクトを取得します。これらのオブジェクトはクリアされません。
自己呼び出しがクロージャ内で使用されない場合、このトリックは発生しません。
もちろん、Swiftは、何らかの理由でAppleがエレガントと呼ぶソリューションを提供します。 ここにあります:
whatToDo = { [unowned self] in println(self.theString) }
出来上がり! 結論:自己呼び出しを含むすべてのクロージャーのライフサイクルに注意してください。
2.配列、ディクショナリ、および構造体のデフォルトは、参照によって渡されることのない非可変タイプです
タスクが新しい言語を非常に迅速に学習することである場合、オートコンプリートがコーディングプロセスで直接必要なすべてを私に教えてくれるという事実に依存して、配列や辞書などの直観的に明らかなタイプでドックを読むことに夢中になります。 それでも、このような急いでのアプローチは、「JSに似た」リンクのセットとして「アレイの配列」と「ストレートの配列」をすべて認識したとき、重要な場所で私を失敗させました。
ドックを読んだ後、私はそれでも一目見ました: Swiftでは、配列と辞書はストラトであり、したがって、他の階層と同様に、参照ではなく値によって(コンパイラーが内部で最適化するコピーによって)送信されます 。
Swiftが用意したメガキャッチを示す例:
struct Person : Printable { var name:String var age:Int var description:String { return name + " (\(age))" } } class ViewController: UIViewController { var teamLeader:Person! var programmers:[Person] = [] func addJoeyTo(var persons:[Person]) { persons.append(Person(name: "Joey", age: 25)) } override func viewDidLoad() { teamLeader = Person(name: "Peter", age: 30) programmers.append(teamLeader) // ... teamLeader.name = "Peter the Leader" addJoeyTo(programmers) // ... , println(programmers) } }
起動時に、誤ってキーを「参照による転送」と考えた場合、コンソールに表示されることが予想されます。
[Peter the Leader (30), Joey (25)] // 1
代わりに、次を確認します。
[Peter (30)] // 2
注意してください! 最初の結果が本当に必要な場合、状況から抜け出す方法は? 実際、 各ケースには個別のソリューションが必要です。 この例では、structをclassに置き換え、[Person]をNSMutableArrayに置き換えるオプションが機能します。
3.シングルトンインスタンス-最高の「ハック」を選択する
キャッチは、現在Swiftのクラスに静的なストアドプロパティを含めることはできず 、静的メソッド(クラスfunc)または静的計算プロパティ(クラスvar x:Int {return 0})のみを持つことです。
![画像](https://habrastorage.org/files/d4f/a31/25b/d4fa3125b1744e9f93bbb6d6731325f9.png)
同時に、Apple自体はシングルトンパターンの精神でグローバルインスタンスに対する偏見を持ちません。NSUserDefaults.standardUserDefaults()、NSFileManager.defaultManager()、NSNotificationCenter.defaultCenter()、UIApplication.sharedなどの真珠を使用してこれを定期的に検証します。 、など。 次回の一般的な更新-Swift 1.2では、静的変数を実際に取得します。
それでは、現在のバージョンのSwiftでこのような独自のインスタンスをどのように作成するのでしょうか? 一般名Nested Structには 、いくつかの「ハッキング」がありますが、最も簡潔なものは次のとおりです。
extension MyManager { class var instance: MyManager { func instantiate() -> MyManager { return ... // } struct Static { static let instance = instantiate() // lazily loaded + thread-safe! } return Static.instance } }
Swift行は、静的に保存されたプロパティをサポートするだけでなく、デフォルトで遅延スレッド指向の初期化を提供します。 これは利益です! これを事前に知らなくても、余分なコードを書いてデバッグする時間を無駄にすることができます。
注意! swiftの次のバージョン(1.2)では、この「ハック」は不要になりましたが、一般リリースの日付は不明です。 (テスト用のベータ版は既に利用可能ですが、このためには、XCode6.3のベータ版も必要です。このビルドはAppstoreからは受け入れられません。要するに、グローバルリリースを待っています。)
4. didSetおよびwillSetメソッドは、コンストラクターの実行中に呼び出されません
些細なことのように思えますが、これを知らない場合は、バグをデバッグするときに完全な混乱に陥ることがあります。 したがって、didSet内で何らかの操作セットを計画している場合、これは初期化中およびオブジェクトのライフサイクル中の両方で重要であり、次のように行う必要があります。
class MyClass { var theProperty:OtherClass! { didSet { doLotsOfStuff() } } private func doLotsOfStuff () { // didSet theProperty } ... init(theProperty:OtherClass) { self.theProperty = theProperty doLotsOfStuff() } }
5.サーバーからの応答時にUIを取得して更新することはできません
ObjCの経験があるプログラマーは、よく知られている必要があるため、この「トラップ」を笑うことができます。UI関連のメソッドは、メインスレッドからのみプルできます。 さもなければ、予測不能性とバグが完全な混乱に陥ります。 しかし、なんらかの理由で、この命令は私が最終的にひどいバグに遭遇するまで通り過ぎました。
「問題」コードの例:
func fetchFromServer() { let url = NSURL(string:urlString)! NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { data, response, error in if (error != nil) { ... } else { self.onSuccess(data) } })!.resume() } func onSuccess(data) { updateUI() }
completionHandlerブロックに注意してください。これらはすべてメインスレッドの外部で実行されます 。 まだ結果に遭遇していない人のために、実験しないことをお勧めしますが、updateUIを次のように配置することを忘れないでください。
func onSuccess(data) { dispatch_sync(dispatch_get_main_queue(), { updateUI() }) }
これは典型的な解決策です。 1行で、updateUIをメインスレッドに戻し、驚きを避けます。
今日は以上です。 すべての新規参入者の成功!
モバイルからの経験豊かなハブロビテス-あなたのコメントは、私とすべての初心者に非常に役立ちます。