質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

3回答

1867閲覧

storyboardのインスタンスを比較したい

balaem

総合スコア13

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2016/07/17 02:23

編集2016/07/19 01:33

###前提・実現したいこと

swiftでのstoryboardインスタンスの比較演算子===について思っていることと挙動が違うので教えて下さい。
該当のソースコードをデバッグすると、同じアドレスを参照してるのに
ifの結果はelseに飛んでしまいます。
どのようにしたら2つのstoryboardを比較できるのでしょうか?

一意で有効なプロパティがあればそちらでも比較してみたいです。
(実際にはないですが、storyboard.nameとかstoryboard.IDのようなものがあれば)

よろしくお願いいたします。

###発生している問題・エラーメッセージ

エラーメッセージはありません。

###該当のソースコード

swift

1ここにご自身が実行したソースコードを書いてください 2 3func receive(vc: UIViewController) { 4 var storyboard = UIStoryboard(name: "Name", bundle: nil) 5 if vc.storyboard === storyboard { 6 7 } else { 8 // vc.storyboardとstoryboardのアドレスは同じなのに処理はこちらにきてしまう 9 } 10}

イメージ説明
イメージ説明

↑2つのアドレスは同じなのに

イメージ説明
結果は違う方に行ってしまう

イメージ説明

イメージ説明

unsafeAddressOfでアドレスを取得

アドレス結果

###補足情報(言語/FW/ツール等のバージョンなど)
Xcode(7.3.1)
iOS_simulator(iphone6s)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

いろいろ確認の仕方を誤って誤解しているような気がするので、コメントします。

まず、vc.storyboardのツールチップ表示とstoryboardのツールチップ表示の値が同じことから、この2つは同じだと判断しているようですが、vc.storyboardのツールチップ表示は、vc.storyboardの値を表示したものではなく、storyboardというローカル変数の値を表示しているだけです。

LLDBコマンドで
po storyboard
po vc.storyboard
の両方を打って確認すれば、ツールチップに表示されているのはstoryboardの方であることがわかると思います。まぁこれはXcodeの表示がわかりにくいと思うので、気持ちはわかりますが、大事な値を確認する時は、ツールチップ表示でなくpoコマンドで確認することをお勧めします。その方が記録も残りますし。

次に、

やはり、vc.storyboardとstoryboardは同じ実体を参照しているようです。
同じ実体だと思ってるのは画像の、0x000000010a7504b0 です。

とのことですが、0x000000010a7504b0は実体(インスタンス)ではありません。
(では、この値は何なのか?と聞かれたら、それはよくわかりません。eコマンドやpコマンドでオブジェクトを表示した時、デバッガーはそれをオブジェクトと認識せずに表示しますから、コンパイラー内部でオブジェクトを形成するための内部メモリ構造がそのまま出力されているのだと思います。その内部メモリ構造については仕様が公開されてないので正確なことはわかりません。オブジェクトを表示するならpoコマンドを使用して確認すべきだと思います。)

===演算子は、実体(インスタンス)が同じものか否かを比較する演算子ですから、
po storyboard
po vc.storyboard
の出力値が同じであれば、同じ実体(インスタンス)と判断されます。

少なくともあなたのコードは、
storyboard = UIStoryboard(name: "Main", bundle: nil)
で、新しい実体(インスタンス)を作っていますから、
それと既存のvc.storyboardの実体(インスタンス)が===で同じと判断されることは絶対にありません。

「比べたいのは、参照先の実在のストーリーボードです。」と書いている意味は、おそらく実体(インスタンス)が同じか否かを比較したいのではなく、同じストーリーボードファイルを使用して生成したものか否かを比較したいのだと思います。
既に調べられているように、どのストーリーボードファイルで使用して生成したものかを示すプロパティはないようです。今回の場合、結局は通常モードで動作しているかメンテナンスモードで動作しているかを確認したいだけのような気がするので、vcのstoryboardを生成する時に、どちらのstoryboardを生成したか(というか現在どちらのモードで動作しているか)をAppDelegateのプロパティとかに覚えておけばよいだけのように思います。

投稿2016/07/19 09:20

TakeOne

総合スコア6299

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

balaem

2016/07/19 16:11

> vc.storyboardのツールチップ表示は、vc.storyboardの値を表示したものではなく、storyboardというローカル変数の値を表示しているだけです。 よくわかりました。こちらが勝手にvc.storyboardのstoryboardにカーソルをあわせれば、うまく解釈してくれてvc.storyboardのアドレスを表示してくれてるものだと思ってたのですが、 そうではなく、vc.storyboardのstoryboard部分がたまたまスコープ内に同名の別物のstoryboardが存在するためにこちらのアドレスが表示されてたわけですね。 これからはツールチップではなくpoコマンドを使用していきます。 > とのことですが、0x000000010a7504b0は実体(インスタンス)ではありません。 ここは言葉が足りなかったです。 0x000000010a7504b0が実体ではないことはわかります。アドレスです。 vc.storyboardのアドレスは0x00007fbc5bd195e0番地で0x000000010a7504b0という値を格納していて、0x000000010a7504b0番地にはなにかがある。 storyboardのアドレスは0x00007fbc5bf92b90番地で0x000000010a7504b0という値を格納していて、同じように0x000000010a7504b0番地にはなにかがある。 つまり、同じものを指し示しているのは事実です。ここを比べたかったのです。 > で、新しい実体(インスタンス)を作っていますから、 それと既存のvc.storyboardの実体(インスタンス)が===で同じと判断されることは絶対にありません。 ここはよくわかりました。 > 同じストーリーボードファイルを使用して生成したものか否かを比較したいのだと思います。 仰るとおりです。同じインスタンスでなくても作成元が同じならばOKです。 > AppDelegateのプロパティとかに覚えておけばよいだけのように思います。 これをすれば確かに実現できると思います。 ですが、わざわざアプリ全体から参照書き換えできるプロパティを一つ用意して、状態を管理して、状態により分岐させて忘れずに状態を書き換えておく、というようなコードは避けたほうがいいかな、と思いstoryboardを利用することを思いつきました。 ですが、比較するプロパティがないのなら、この思いつきがあまり良くなかったようです。 今のところは、AppDelegateにlast_statusCodeプロパティを用意して実装しようと思います。 _Kentarou様、fuzzball様、TakeOne様 ご教授ありがとうございました。 これからはpoコマンドを使っていきます!
guest

0

検証は全く行っていませんので、ただのアイデアだと思って下さい。

isEqual

swift

1if (vc.storyboard.isEqual(storyboard)) { 2 print("equal") 3}

.hash

一意で有効なプロパティになるのかどうか分かりませんが。

swift

1if (vc.storyboard.hash == storyboard.hash) { 2 print("equal") 3}

投稿2016/07/19 02:15

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

balaem

2016/07/19 03:59

fuzzball様、ご回答ありがとうございます。 isEqualも.hashもうまくいきませんでした。 isEqualは、ステータスコードにより作られるストーリーボードの組み合わせを変えてみましたが、 すべて場合でif文ではelse側にいってしまいます。 .hashは数値自体が違いました。
fuzzball

2016/07/19 04:17

残念。 再現できる環境を作れたら調べてみるのですが、面倒臭そうなのでこれ以上はパスです。
balaem

2016/07/19 14:02

ヒントを頂けただけでも嬉しいです。ありがとうございました。
guest

0

該当のソースコードをデバッグすると、同じアドレスを参照してるのに

ifの結果はelseに飛んでしまいます。

こちらはアドレスは違うと思います、デバックでそれぞれのアドレスを確認してみてください。
e vc.storyboarde storyboardで両方出力してみると分かると思います。

こちらのメソッドは渡されたViewControllerStoryboardを比較していますが、Storyboardが自分自身のいるStoryboardとして比較しているのでしたら、この場合はStoryboardのインスタンスを新たに作っているので同じではないです。

ロジック自体はインスタンスを渡せばちゃんと判定できています、このメソッドを使用したい状態とはどのような時でしょうか?

アドレスが同じと確認したコード

swift

1import UIKit 2 3class ViewController1: UIViewController { 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 } 8 9 @IBAction func pushNextButton(sender: UIButton) { 10 11 let nextStoryboard = UIStoryboard(name: "Main2", bundle: nil) 12 let vc2 = nextStoryboard.instantiateInitialViewController() as! ViewController2 13 vc2.myStoryboard = nextStoryboard 14 self.navigationController?.pushViewController(vc2, animated: true) 15 } 16} 17 18 19class ViewController2: UIViewController { 20 21 var myStoryboard: UIStoryboard! 22 23 override func viewDidLoad() { 24 super.viewDidLoad() 25 26 receive(self) 27 } 28 29 func receive(vc: UIViewController) { 30 31 if vc.storyboard === myStoryboard { 32 print("同じ") 33 //=> 同じ 34 } else { 35 print("違う") 36 } 37 } 38}

投稿2016/07/17 08:06

編集2016/07/17 12:17
_Kentarou

総合スコア8490

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

balaem

2016/07/17 11:01

> e vc.storyboardとe storyboardで両方出力してみると分かると思います。 この結果は確かにアドレスは違いますが、それは通常の挙動だと思います。 というのは、引数で渡ってきたvc.storyboardの参照元は、それはそれでメモリを確保していて、実アドレスがあって、という認識で ローカルで作ったstoryboardにも参照元は、メモリを確保していて実アドレスがあって、 だと思うので、lldbのeの結果が違うのはわかります。 比べたいのは、参照先の実在のストーリーボードです。 デバッグ中にvc.storyboardとstoryboardにカーソルを持って行くと 同じアドレスが表示されます。 具体的には<UIStoryboard: 0x7fe971638cd0>と表示されてます。 もちろん実行のたびに数値は変わります。 このように参照先のアドレスで比較できないのなら、他の方法でも構わないのですが 有効なプロパティが見つかりません。 それこそ、インスタンス自体は別物だとしても、storyboard.nameとかstoryboard.tag のようなもので比較したいのです。
_Kentarou

2016/07/17 12:24

再現できるコードを載せていただくのが解決が早いとおもいますが、とりあえず参照で渡してアドレスでの比較はできることは確認しました。 Storyboardクラスには比較できるようなプロパティはないので、現時点では参照で比較するしかないと思います。 そもそもですがどのような場合にStoryboardを比較するようなことをしたいのでしょうか? 表示しているViewControllerで同じ事はできないのでしょうか?
balaem

2016/07/17 21:08 編集

なぜ、storyboardを比較する必要があるか、ですが 順を追って説明します。 まず、最初のビューが表示される前にAlamofireでAPIにアクセスして statusCodeが200ならば通常のmainStoryboardのmainViewControllerを表示、 statusCodeが503ならば、メンテナンスモードを伝えるための、maintenanceStoryBoradのmaintenanceViewControllerを表示する、 という処理を書こうとしました。 ところが、アプリのライフサイクルとして、最初のmainViewControllerが表示される前に APIにアクセスしてstatusCodeを判断したいと思いましたが、AppDelegateのdidFinishLaunchingWithOptionsにAlamofireの.GETで取得するコードを書いても、そのコードはなぜか飛ばされてしまったのです。 なので、しかたなく現状はmainStoryBoardのmainViewControllerのdidBecomeActiveでAlamofireを使ってstatusCodeを取得しています。 このコードはAppDelegateに書いたコードと同じでもdidBecomeActiveに書いた時はコードは飛ばされずに、ちゃんと処理してくれます。 didBecomeActiveの処理はprotocolを使って、どのviewControllerからも同じ処理がよばれるようになってます。 didBecomeActiveに書いてるので、ホーム画面からアプリに復帰した場合に、mainStoryBoardのmainVeiwControllerが表示されている時と、 maintenanceStoryBoardのmaintenanceViewControllerが表示されている時があるわけです。 ここまでなら、viewControllerを比較すればよいのですが、mainStoryBoardにはsecondViewControllerがあります。 なので、storyboardで比較するのが良いかと思った次第です。 処理を分けたいのはどのviewControllerが表示されているか、ではなく どちらのstoryboardを使っているか、なのです。 そもそも、AppDelegateでstatusCodeを取得できれば、こんなことにはならずに済むかもしれません。 _Kentarou様が確認していただいた アドレスが同じと確認したコード を当方で再現してみたところ、func nextの2行目 let vc2 = nextStoryBoard ~ で、エラーになってしまいました。 エラーメッセージは fatal error: unexpectedly found nil while unwrapping an Optional value です。 このエラーになってしまったコードは↓こちらです。 https://github.com/balaem/sample_sb_hikaku storyboardを比較するような処理を書こうとしてるのが 一般解ではないのかもしれませんね。
_Kentarou

2016/07/17 23:13

statusコードのチェックは毎回の起動時(バックフォアを含む)に実行されるものですか? --- サンプルに関して --- エラーが出ているのはMain2.storyboardにあるViewController2のis initial View Controllerにチェックが入っていないことが原因です。 ※Pushで遷移するためViewController1にNavigationControllerを付けてください。
balaem

2016/07/18 01:48

> statusコードのチェックは毎回の起動時(バックフォアを含む)に実行されるものですか? フォアグラウンドだけで大丈夫です。 --- サンプルに関して --- https://github.com/balaem/sample_sb_hikaku ナビゲーションコントローラなどの追加をしました。 _Kentarou様のコードで比較演算子===は2つのストーリーボードが同じものとして処理されてます。デバッグで"同じ"と表示されます。 でもそれは、func recieveにselfを渡していて(selfはViewController2で呼んでいるので必ずViewController2。つまり引数のvc.storyboardは必ずViewController2のプロパティのstoryboard)プロパティmyStoryBoardはViewController2のプロパティなので、vc.storyboardと一致して当然だと思います。 このサンプルコードで、ifのelse側に来ることは絶対にありえないわけです。 私が最初に書いたコードとは、違うもののようです。 質問していることは、参照先のアドレスが同じなのに なぜ、比較演算子===の挙動が思った通りにならないのか、ということです。 私が、参照先のアドレスだと思ってるものが、実は違ってたりするのかな、というような疑問なのです。 ソースの画像を送りたいのですが、コメントでは送れないのでしょうか?
_Kentarou

2016/07/18 01:57

> ソースの画像を送りたいのですが、コメントでは送れないのでしょうか? 載せて大丈夫な画像でしたら、質問に貼り付けていただければ良いとおもいます。 確かに自分のサンプルは簡単でもっと複雑な事をされている事は分かったいますが、同じアドレスで違う挙動をすることはないと思うので、何処にどの様に保持していて使うときにdのように持ってきているのか知りたいと思います。
balaem

2016/07/18 02:28

> 載せて大丈夫な画像でしたら、質問に貼り付けていただければ良いとおもいます。 ありがとうございます。 ソースの画像をupしました。
_Kentarou

2016/07/18 03:40

同じ事が再現するところまで作ってみました。 画像に載ってい事は実際に起こっているのですが、それぞれのオブジェクトをprintするとアドレスは違いましたので===の判定は合っていると思います。 ※そちらの状況とは違うかもしれません。 この様な実装はしたことがないので詳しくは分からないです。 こちらのプロトコルはmainViewControllerとmaintenanceViewControllerで使用していると思っていますが(mainViewControllerのあるStoryboardに配置しているもう一つのViewControllerは適用されていない想定)その場合は単純にStoryboardではなく渡されてくるViewControllerのクラスで判定するのではダメでしょうか?
balaem

2016/07/19 01:34

> その場合は単純にStoryboardではなく渡されてくるViewControllerのクラスで判定するのではダメでしょうか? protocolはmainStoryBoardのsecondViewControllerにも適用してますので、storyboardで比較するのがスマートだと思ったのです。 protocolをすべてのviewControllerに適用してるのは、アプリがフォアグラウンドに来る可能性はすべてのviewControllerであり得るからです。 ですので、whetherDoMainteに渡ってくるvcはすべてのviewControllerの可能性があります。viewControllerで条件分岐するのは煩雑ですし、今後viewControllerが増えればそのたびに条件分岐のところを修正する必要があります。 どちらのstoryboardに所属しているかがわかればviewControllerが増えたとしても修正する必要がありません。 > それぞれのオブジェクトをprintするとアドレスは違いましたので こちらでもprintしてみました。 unsafeAddressOfを使って参照元と参照先のアドレスも表示してみました。 この確認画像もup済みです。 やはり、vc.storyboardとstoryboardは同じ実体を参照しているようです。 同じ実体だと思ってるのは画像の、0x000000010a7504b0 です。 比較演算子===の挙動が全く納得いきません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問