2017-01-27 5 views
2

あなたは、VC(緑)を持っており、それがパネル(黄色)「ホルダー」このビューコントローラは "willSet/didSet"ペアでリークしますか?

enter image description here

あなたは10の異なるビュー・コントローラ...価格、販売、証券、トラック、ドライバー、パレットがあるとしていますあなたはイエローエリアに一度に1つずつ入れようとしています。これは、動的に我々がcurrentに現在のVC 1を保持するストーリーボード

instantiateViewController(withIdentifier: "PricesID") as! Prices 

から各VCをロードします。ここでは、それらの "スワップ"を可能にするコードです...

>>これは間違っています。このコードを使用しないでください< <

サルタンが以下で説明することをしなければなりません。

var current: UIViewController? = nil { 
    willSet { 
     // recall that the property name ("current") means the "old" one in willSet 
     if (current != nil) { 
      current!.willMove(toParentViewController: nil) 
      current!.view.removeFromSuperview() 
      current!.removeFromParentViewController() 
      // "!! point X !!" 
     } 
    } 
    didSet { 
     // recall that the property name ("current") means the "new" one in didSet 
     if (current != nil) { 
      current!.willMove(toParentViewController: self) 
      holder.addSubview(current!.view) 
      current!.view.bindEdgesToSuperview() 
      current!.didMove(toParentViewController: self) 
     } 
    } 
} 

>>>>>>>>重要! < < < < < < < < <

また、あなたはこのような何かをすれば、緑のページが行われたときに、黄色のビューコントローラを取り除くために不可欠であり、注意してください。 currentがそれを保持し、緑のページが解放されることはありませんそれ以外の場合:

override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { 
    current = nil 
    super.dismiss(animated: flag, completion: completion) 
} 

継続、あなたはこのようにcurrentプロパティを使用したい:

func showPrices() { 
    current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices 
} 
func showSales() { 
    current = s.instantiateViewController(withIdentifier: "SalesID") as! Sales 
} 

しかし、予告「点X」を考えます。通常は、削除するView Controllerを必ずnilに設定します。

blah this, blah that 
blah.removeFromParentViewController() 
blah = nil 

しかし、私は実際には "willSet"コードブロックの中に現在の値を設定することはできません。そして、私はそれがちょうど何か(didSetの中)に設定されようとしていることに感謝します。しかし、それはちょっと変わったようです。何が欠けている?あなたは計算されたプロパティでこのようなことをすることができますか?


最終使用可能なバージョン..... Sulthanのアプローチを使用して

、これは、かなりのテストの後に完璧に動作します。だから、これはうまく機能

このような
// change yellow area to "Prices" 
current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices 

// change yellow area to "Stock" 
current = s.instantiateViewController(withIdentifier: "StickID") as! Stock 

を呼び出す

...

var current: UIViewController? = nil { // ESSENTIAL to nil on dismiss 
    didSet { 
     guard current != oldValue else { return } 

     oldValue?.willMove(toParentViewController: nil) 
     if (current != nil) { 
      addChildViewController(current!) 

      holder.addSubview(current!.view) 
      current!.view.bindEdgesToSuperview() 
     } 
     oldValue?.view.removeFromSuperview() 

     oldValue?.removeFromParentViewController() 
     if (current != nil) { 
      current!.didMove(toParentViewController: self) 
     } 
    } 
    // courtesy http://stackoverflow.com/a/41900263/294884 
} 
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { 
    // ESSENTIAL to nil on dismiss 
    current = nil 
    super.dismiss(animated: flag, completion: completion) 
} 
+1

セッターオブザーバーを使用しているということは、プロパティーが何かに設定されているということです。つまり、nilか他のビューコントローラーであるということです。では、 'willSet'と' didSet' makeの間の中間段階で 'nil'をどのような違いで割り当てることができるでしょうか?また、保持サイクルを持たない限り、明示的に 'nil'への参照を設定する必要はありません - 保持カウントは範囲外になったときに減少し、強い参照が残っていなければ割り当てが解除されます。 – Hamish

+0

@ハミッシュ...「違いはな... ...まあ、スマートな人とチェックするのはいつも良いことです。 :) – Fattie

+0

@ハミッシュ - あなたの2番目のポイントについて。うーん、現在の*はそれに対する強い参照です。プロパティを脇に置いておきます。クラスのある時点で "current = instantiateViewController ..."とすると、最終的に結局そのようなことはありません。単にremoveFromParentViewController() 'はそれを解放しません、私は信じて???? – Fattie

答えて

2

:私は、これはあなたがやろうとしているものをはるかに優れていと思います。しかし、最大の問題は、コードをwillSetdidSetの間で分割しようとしているということです。なぜなら、これはまったく必要ないからです。ところで

var current: UIViewController? = nil { 
    didSet { 
     guard current != oldValue else { 
      return 
     } 

     oldValue?.willMove(toParentViewController: nil)    
     if let current = current { 
      self.addChildViewController(current) 
     } 

     //... add current.view to the view hierarchy here... 
     oldValue?.view.removeFromSuperview() 

     oldValue?.removeFromParentViewController() 
     current?.didMove(toParentViewController: self) 
    } 
} 

、関数が呼び出される順序が重要です:あなたは、常にoldValuedidSetで使用することができます。したがって、私は機能をremoveaddに分割するよう勧めません。そうでなければ、両方のコントローラのためにviewDidDisappearviewDidAppearの順番が驚くかもしれません。

+0

@JoeBlowもちろん、あなたがこれを使用している場合、アニメーションを持つことはできません。トランジションをアニメーション化することで、アニメーションブロックにビューの変更を入れ、最後の2行をコンプリートブロックに入れます。是非とも – Sulthan

+0

これはアニメのない状況です! – Fattie

+0

こんにちは@Sulthan! **あなたの言うことは100万パーセント正しい**です。私の質問のコードは動作しません。あなたは、あなたが言うようにコールをインターリーブする必要があります。感謝の言葉を少し払いました – Fattie

3

はのは、二つに質問を分割してみましょう:(1) "リーク" はありますか? (2)これは良い考えですか?

最初に「リーク」。短い答え:いいえ。 currentnilに設定しない場合でも、明らかに保持するビューコントローラは「リーク」しません。含まれているビューコントローラが存在しなくなると、ビューコントローラはcurrentを指します。

currentビューコントローラは、必要以上に長生きします。そのため、これは愚かなことです。子ビューコントローラーに強い参照が必要はありません。なぜなら、それはあなたのchildViewControllers[0]です(子ビューコントローラー「ダンス」を正しく行う場合)。したがって、あなたの財産を複製するだけで、childViewControllersプロパティはすでにとなります。

これは、2番目の質問につながります。それは良いアイデアをしているのですか?いいえ、どこから来ているのかわかります。子ビューコントローラの「ダンス」をカプセル化したいと思っています。しかし、あなたはどんな場合でもダンスを間違ってやっています。このようにView Controllerの階層を覆しています。 「ダンス」をカプセル化するために、私はあなたが正しくダンスをやって、それが存在する場合はchildViewController[0]を指し計算読み取り専用プロパティと一緒に、それを実行機能を供給オフにはるかに優れていると言うでしょう。

ここでは、一度に1つの子ビューコントローラしか持たないと仮定します。私はdidSetを使用して、実際に間違っているとは思わない

var current : UIViewController? { 
    if self.childViewControllers.count > 0 { 
     return self.childViewControllers[0] 
    } 
    return nil 
} 

func removeChild() { 
    if let vc = self.current { 
     vc.willMove(toParentViewController: nil) 
     vc.view.removeFromSuperview() 
     vc.removeFromParentViewController() 
    } 
} 

func createChild(_ vc:UIViewController) { 
    self.removeChild() // There Can Be Only One 
    self.addChildViewController(vc) // * 
    // ... get vc's view into the interface ... 
    vc.didMove(toParentViewController: self) 
} 
+0

facepalm!私は 'childViewControllers'を完全に忘れていました:Oありがとう、男性 – Fattie

+0

そうですが、私はあなたの質問が馬鹿になるとは思わない。 「ダンス」をカプセル化することは、すばらしいアイデアです。さもなければ、私はそのコードを書く時間をとらなかっただろう! – matt

+0

これはいくつか考えられます。スクリーンがあるとします。そしてそれは5人の子VCと言います。たとえば、10個の異なるディスプレイ(私の例のように価格/売上など)の間を循環する1つの領域(「displayX」)があります。その時点でdisplayX内のものを「見つける」必要がありますか?これは 'childViewControllers'配列の#3と言えます。あなたが 'childViewControllers'配列の' removeFromParentViewController' item#3を削除したときに、それを知っていますか?それは配列からですか?または何?これは何らかの調査を要するかもしれません! :もう一度ありがとう! – Fattie

関連する問題