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

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

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

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

Q&A

解決済

2回答

950閲覧

タイププロパティ(静的プロパティ)の再評価

pegy

総合スコア245

Swift

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

0グッド

0クリップ

投稿2020/08/23 10:00

(環境)
xcode:11.3
version 5.1.3

(参考文献)
詳解 Swift 第5版
著者 萩原剛志
発行者 SBクリエイティブジャブ式会社
P084-085

上記の書籍において、タイププロパティの説明について、以下のコードのdisplay2.show()stdWidthdisplay3.show()の結果から変化がないことについて、この様に言及されています。

タイププロパティび初期化のための式は、値が必要とされて初めて評価され、その後は使われないことが原因です。

ここからstdSizeは最初のインスタンスを生成するときに初期化されたあとは、例えばdisplay3の様に新しい、インスタンスを生成しても、再評価はされない(改めて初期化はされない)と読み取れました。なぜこの様な仕様になっているか理由はわからないのですが、例えば一番最後いいかのコードを加えると
print(LCD.stdWidth)
当然、2560が改めて初期化(再評価)され表示されると思います。

同じタイププロパティ、stdWidthstdSize再評価されるとされないのは違いはなんでしょうか?stdSizeは数式だから?間接的に値が決まるから?

同じタイププロパティでもなぜ、この様に違いが生じるのか分かる方がいればご教示ください。
※もっと言ってしまうと、display1インスタンスを生成した時点で初めてタイププロパティstdSizeが評価されて、それで最後にして使われなくなるとも解釈できるので、上記の引用文の通りであればdisplay2display1と同じ結果が出力される様な気もするのですが・・・・

swift

1var serialNumber = 2127 2 3struct LCD { 4 struct Size { 5 var width, height:Int 6 } 7 static var stdHeight = 1080 8 static var stdWidth = 1920 9 static var stdSize = Size(width: stdWidth, height: stdHeight) 10 11 static func sn() -> Int { 12 serialNumber += 1; 13 return serialNumber 14 } 15 let size: Size 16 let serial = "CZ:" + String(LCD.sn()) 17 18 init(size: Size = LCD.stdSize) { 19 self.size = size 20 } 21 func show() { 22 print(serial, "((size.width)x(size.height))") 23 } 24 25} 26 27 28let display1 = LCD(size: LCD.Size(width:800, height:600)) 29display1.show()//CZ:2129 (800x600) 30LCD.stdHeight = 1200 31let display2 = LCD() 32display2.show()//(1920x1200) 33LCD.stdWidth = 2560 34let display3 = LCD() 35display3.show()//(1920x1200)

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

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

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

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

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

guest

回答2

0

ベストアンサー

swift

1var x = 1 2var y = x + 1 3x = 1000

と書いて、y2のままであることと理屈は全く一緒です。


ここからstdSizeは最初のインスタンスを生成するときに初期化されたあとは、例えばdisplay3の様に新しい、インスタンスを生成しても、再評価はされない(改めて初期化はされない)と読み取れました。

違います。

タイププロパティび初期化のための式は、値が必要とされて初めて評価され

stdSizeが一番最初に値が必要とされるのは、引数なしのinit() (let display2 = LCD())中で
LCD.stdSizeと出てくるところです。
このとき初めてstdSize = Size(width: stdWidth, height: stdHeight)が評価(= これはどんな値なんだ?と計算すること)されます。
(display1のときは引数ありなのでstdSizeの値は必要とされない)

さてstdSizeの評価です。
static var stdSize = Size(width: stdWidth, height: stdHeight)とあり、

stdWidthについては実行順としては初めて出てくるので
static var stdWidth = 1920
で初期化されます。

stdHeightはこれより前に
LCD.stdHeight = 1200
と代入しているのでこの値を使います。

投稿2020/08/24 06:53

編集2020/08/24 06:55
ozwk

総合スコア13551

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

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

pegy

2020/08/25 01:18

コメントありがとうございます。例がとてもわかりやすかったです。 stdSizeが初めて利用されるのは全項目イニシャライザのdisplay1ではなく、display2のときなんですね。確かに init(size: Size = LCD.stdSize) { self.size = size } で直接、LCD(size: LCD.Size(width:800, height:600))の様に引数で指定しているので、 static var stdSize = Size(width: stdWidth, height: stdHeight)はdisplay1はまだ評価されていないというところは理解することができました。 とりあえず、初期化され、一度評価されると、storedPropertyの場合にはFIXしてしまう(他にインスタンスを作ろうが)ものと理解いたします。 よろしくお願い申し上げます。
guest

0

Swift

1let display1 = LCD(size: LCD.Size(width:800, height:600)) 2display1.show() // CZ:2128 (800x600) 3LCD.stdHeight = 1200 4let display2 = LCD() 5display2.show() // CZ:2129 (1920x1200) 6LCD.stdWidth = 2560 7let display3 = LCD() 8display3.show() // CZ:2130 (1920x1200)

たとえばこのように実行してきたときに

Swift

1print(LCD.stdWidth) // 2560

の一行を追加した場合には、結果として 「2560」 が表示されます。
これは、

Swift

1LCD.stdWidth = 2560

を実行しているため、当然の結果となります。

ただし、ここで気をつけなければいけないのは、stdWidth を初期化のために利用している他のプロパティについては「初期化されない」ということです。

ちなみに、

当然、2560が改めて初期化(再評価)され表示されると思います。

と表現されていますが、途中で行っている

Swift

1LCD.stdWidth = 2560

は値の代入であり、「初期化(再評価)」ではない という点です。
おそらく、ここが混乱の原因かとおもいます。

初期化(際評価)ではないため、stdWidth を使っている次の行には変化はありません。

なので、

Swift

1print(LCD.stdSize) // Size(width: 1920, height: 1200)

を実行してもheightには影響は及ばず、1200のままとなっています。

同じタイププロパティ、stdWidthとstdSize再評価されるとされないのは違いはなんでしょうか?stdSizeは数式だから?間接的に値が決まるから?

とありますが、タイププロパティであれば定数を代入していても式を代入していても振る舞いは同じです。

たとえば、別の例を考えてみたいと思います。
以下のような構造体をつくります。

Swift

1struct test { 2 static var valA = 1 3 static var valB = valA 4}

次に、以下のように実行すると、結果はコメント通りになります。

Swift

1print(test.valA) // 1 2test.valA = 2 3print(test.valA) // 2 4print(test.valB) // 2 ** 注1

しかし、次の例だと「注1」の部分の結果が異なってきます。

Swift

1print(test.valA) // 1 2print(test.valB) // 1 ** 注2 3test.valA = 2 4print(test.valA) // 2 5print(test.valB) // 1 ** 注3

前者の「注1」の部分は、test.valA = 2 の結果を受けて、表示は 2 となっています。

しかし、後者の「注3」では、test.valA = 2 を実行しているにもかかわらず、評価は 1 となっています。

この理由こそ、

タイププロパティび初期化のための式は、値が必要とされて初めて評価され、その後は使われないことが原因です。

に他ならないかと思います。

つまり、後者の例ではvalB は「注2」で登場しており、そこで初期化が行われます。しかし、ここで一度初期化が行われてしまったため、valAに別の値が代入されても再評価されることなく、「注3」では1が評価される結果となってしまいます。

わかりにくい結果かもしれませんが、評価のタイミングということを考えれば、これは仕様通りの振る舞いとなります。

また、アクセスするたびに他の変数の値に基づいて再評価させたい場合には、P.085 にある通り計算型プロパティを使うことになります。

投稿2020/08/24 04:28

TsukubaDepot

総合スコア5086

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

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

pegy

2020/08/25 01:38 編集

コメントありがとうございます。 " 値の代入であり、「初期化(再評価)」ではない という点です。おそらく、ここが混乱の原因かとおもいます。" と実際の例示でなんとなくイメージを掴むことができました!あまり、変数や定数を使用するときにどのタイミングでそれが初期化されているのか?ということを気にしたことはなかったのですが、そういうことなんですね。 また、動作も確認してみて驚いたのですが例示の print(test.valB) // 1 ** 注2 を入れるだけで、確かにvalBはの値は変わらなくなりました。 実際に提示したコードではstdSizeが利用されるという点では、値が代入される=初期化されているというイメージが湧くのですがprint(test.valB)という関数を実行する=値が代入される=初期化されているという動作なのですね。 当然値が代入されていなければprintでコンソールに出力することができないのは分かるのですが、そのトリガーはコード上で=の代入演算子でstatic var valB = valAと記載することではないんですね。。。。 逆にprint(test.valB)という様にvalBを変数として利用することをしなければ、コード上でstatic var valB = valAを書いたとしてもvalBは実際には何も代入されていないし初期化もされていないということなのでしょうか? 極端な話ですが var a = "おはようございます" var b = "今日は" var c = "とてもいい" var d = "天気ですね" var e = "どこに出かけますか?" print (e) というコードを書いたとしてら、swift上は実際には ① var a,b,c,dは初期化されていない ② var a,b,c,dは値も代入されていなければ、 ③ var a,b,c,dはメモリ上何も割り当てられていない これはどれも正しいのでしょうか? 特に③が少し謎なのですが、 1. 変数の宣言 2. メモリの割り当て 3. 値の代入(初期化) という動作が正しいのか、それとも 値の代入(初期化)をしなければそもそも変数の宣言をしてもメモリ自体が割り当てられないのか、素人ながら興味が湧いてきました。少し自分でも調べてみます。
TsukubaDepot

2020/08/25 02:11

コメント最後のご質問ですが、これはコンパイラの実装や最適化レベルに依存するかもしれません。 この場合、変数a,b,c,d,eはそもそも使い必要がない変数なので、存在しないものとしてコンパイルされる可能性も否定できません。 調べるとなると、Swiftの実装規約を見る必要があるかもしれませんね。
pegy

2020/08/26 08:39

了解しました、ありがとうございます!
TsukubaDepot

2020/08/26 09:05

ごめんなさい、8/25 11:11 に間違いがありました。変数eは使われているので当然コンパイルされます。コンパイルされる際にコードに反映されるか否か分からないのはa,b,c,dの4つだけです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問