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

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

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

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

Q&A

解決済

1回答

642閲覧

キーに文字列を代入する意味

pegy

総合スコア243

Swift

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

0グッド

0クリップ

投稿2020/03/31 08:34

編集2020/03/31 22:39

(環境)
xcode:11.3
version 5.1.3

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

上記の参考文献からSwiftを学んでおります。下記に秒数をカウントダウンしてcountDownLabelに残秒数を表示させるサンプルコードで一点わからない点があります。

let settingKey = "timer_value" //ここです
の定数の宣言なのですが、ここでsettingKeyの定数に文字列の"timer_value"を代入することに何か意味があるのでしょうか?
settings.register(defaults: [settingKey:10])let timerValue = settings.integer(forKey: settingKey)で初期値として10を登録したり取り出したりする際に、実際にはsettingKeyをキー(ラベル)の様な使い方をしており、特に何かの文字列を代入することに意味がない様に思えるのですが、文法的な意味合いがわかればご教示いただけると嬉しいです。

Swift

1 2import UIKit 3 4class ViewController: UIViewController { 5 6 var timer : Timer? 7 var count = 0 8 let settingKey = "timer_value" //ここです 9 10 override func viewDidLoad() { 11 super.viewDidLoad() 12 // Do any additional setup after loading the view. 13 let settings = UserDefaults.standard 14 settings.register(defaults: [settingKey:10]) 15 } 16 17 @IBOutlet weak var countDownLabel: UILabel! 18 19 20 @IBAction func settingButtonAction(_ sender: Any) { 21 // timerをアンラップしてnowTimerに代入 22 if let nowTimer = timer { 23 // もしタイマーが、実行中だったら停止 24 if nowTimer.isValid == true { 25 // タイマー停止 26 nowTimer.invalidate() 27 } 28 } 29 // 画面遷移を行う 30 performSegue(withIdentifier: "goSetting", sender: nil) 31 } 32 33 @IBAction func startButtonAction(_ sender: Any) { 34 // timerをアンラップしてnowTimerに代入 35 if let nowTimer = timer { 36 // もしタイマーが、実行中だったらスタートしない 37 if nowTimer.isValid == true { 38 // 何も処理しない 39 return 40 } 41 } 42 43 // タイマーをスタート 44 timer = Timer.scheduledTimer(timeInterval: 1.0, 45 target: self, 46 selector: #selector(self.timerInterrupt(_:)),//ここです 47 userInfo: nil, 48 repeats: true) 49 50 } 51 52 @IBAction func stopButtonAction(_ sender: Any) { 53 // timerをアンラップしてnowTimerに代入 54 if let nowTimer = timer { 55 // もしタイマーが、実行中だったら停止 56 if nowTimer.isValid == true { 57 // タイマー停止 58 nowTimer.invalidate() 59 } 60 } 61 } 62 63 func displayUpdate() -> Int { 64 65 // UserDefaultsのインスタンスを生成 66 let settings = UserDefaults.standard 67 // 取得した秒数をtimerValueに渡す 68 let timerValue = settings.integer(forKey: settingKey) 69 // 残り時間(remainCount)を生成 70 let remainCount = timerValue - count 71 // remainCount(残りの時間)をラベルに表示 72 countDownLabel.text = "残り(remainCount)秒" 73 // 残り時間を戻り値に設定 74 return remainCount 75 76 } 77 78 // 経過時間の処理 79 @objc func timerInterrupt(_ timer:Timer) { 80 81 // count(経過時間)に+1していく 82 count += 1 83 // remainCount(残り時間)が0以下のとき、タイマーを止める 84 if displayUpdate() <= 0 { 85 // 初期化処理 86 count = 0 87 // タイマー停止 88 timer.invalidate() 89 } 90 } 91 92 override func viewDidAppear(_ animated: Bool) { 93 // カウント(経過時間)をゼロにする 94 count = 0 95 // タイマーの表示を更新する 96 _ = displayUpdate() 97 } 98 99 100} 101 102

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

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

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

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

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

MasakiHori

2020/03/31 08:51

質問は1ずつ行ったほうが回答を得やすいです
pegy

2020/03/31 22:38

コメントいただきありがとうございます。 質問を分けて投稿させていただきます。 よろしくお願い申し上げます。
guest

回答1

0

ベストアンサー

回答を修正/変更


UserDefaultsは「辞書」と呼ばれる構造を持ったクラスです。
辞書は二つの値を持つペアを複数持った構造です。
ややこしいですがSwiftのDictionayも辞書構造をもつstructです。

辞書は文字通り索引(項目名)とその内容を持った構造で、索引から内容を取得するものです。
この索引に当たるものをKeyと呼びます。取り出した値はそのままValueです。

UserDefaultsはキーとして文字列を使用することとなっています。
つまり"timer_value"は辞書のキーということになります。
もちろん別のキーを使うと全く無関係の値を登録することが出来ます。

またUserDefaultsにはデータを永続化(アプリを終了した後でも前回のデータを記憶)する機能も備わっていますので、このような場面でよく使われます。

なおUserDefaultsは大容量のデータを永続化することを想定していないクラスですので、何でもかんでもUserDefaultsを使うのはやめましょう。


質問の意図とずれているかもしれませんが、typo(入力間違い)によるバグを発生させにくくしたり、変更/修正を容易にするためです。

定数に代入することなく直接文字列を書けばいいのですが、そうすると、文字列の間違いをコンパイラは検出できないため、typoによるバグが発生する温床になります。

また、文字列を変更/修正する必要が生じた時、定数を利用していなければ、すべての対象文字列を確実に変更/修正しなければなりません。
これもバグの温床となります。
定数に代入して利用していれば、変更は1か所のみとなります。

このような理由で、文字列を使いたい場合は定数に代入しそれを利用するようにします。

※ 例外はあります
ex. fatalErrorの引数とか

投稿2020/03/31 09:06

編集2020/04/01 01:03
MasakiHori

総合スコア3384

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

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

pegy

2020/03/31 11:34

コメント頂き有難うございます。変数化又は定数化する事でバクを未然に防止するという趣旨を理解致しました。 そういう意味では"timer_value"自体にはあまり意味はなく let settingKey: String? だけの宣言でも良い気がするのですが、如何でしょうか? "timer_value"という文字列が何も使われていないことへの不思議に囚われすぎているのかもしれません。。 重ねて申し訳ございませんが、宜しくお願い申し上げます。
MasakiHori

2020/04/01 00:41

そちらの疑問でしたか 回答変更します
pegy

2020/04/01 01:53 編集

コメント改めてありがとうございます。 dictionaryという構造を持つクラスであることはわかり、どの様な型であるのか参考書で確認しました。 どうしても納得できない部分があるのですが。 settings.register(defaults: [settingKey:10])として 最終的には永続的にアクセスしたいのデータは10という数値であり let settingKey = "timer_value" defaults: [settingKey:10] の繋がりが見えづらく、ここで任意の文字列"timer_value"の存在が任意のどの値を入れてもどうせ使わないのにと思ってしまいます。 結局数値の10アクセスするためには[settingKey:10]であれば足りると思っており 頭の中の構造では↓の様なイメージでおります。 let userDefaults: Dictionary<String, Int> = ["settingKey": 10] おそらく何か根本的な勘違いをしているのかもしれませんが、ご指摘願えますでしょうか。
pegy

2020/04/01 01:56

辞書のキーであるとすると、実際には ["timer_value": 10] ということではあるが、これを定数かして汎用的に使用することと、冒頭おっしゃっていたバグの温床を回避する目的で settingKey = "timer_value" に置き換えて使用しているという解釈で正しいでしょうか?
MasakiHori

2020/04/01 03:30

最初に起動したときにはsettingKeyに対応する値は存在していません。 しかし、settings.register(defaults: [settingKey:10])のようにしておくと値が存在しなかった時にここで登録した値が返されるようになります。 ここはそのためのコードです。 おそらく読み進めるとユーザーが任意の秒数に変更できるようにするのだと思いますが、この時 10 を定数としておくとその対応が出来ません。 UserDefaultsを使っていればユーザーが任意に秒数を変更した時にその値をUserDefaultsに保存することで、ユーザーが指定した秒数で動くようになります。
pegy

2020/04/01 04:04

ありがとうございます。あまりうまくお伝えできずに申し訳ないでのですが、 おっしゃる流れな概ね理解することができました。また動作としてもご推察の通りなのですが、そのご説明の過程でも一度もtimer_valueは出てこないと思います。 [settingKey:10]であれば足りるので なぜ、↓の様に使いもしない文字列をキーの値に代入しているのでしょうか? settingKey = "timer_value" 実際にキーとして呼び出しをする時も let timerValue = settings.integer(forKey: settingKey) なので、settingKey = "timer_value"のコードが意味がないのではと思っています。結局、辞書型のクラスから呼び出すときのキーはsettingKeyなのですよね?なぜこのコードは、その後、利用されることもない、任意の"timer_value"を代入してみようと思ってのでしょうか?
pegy

2020/04/01 04:09

例えばJavascriptのオブジェクトを作るとき const obj = { color: "red" getColor:function(){ return "色:" + this.color; } } の様に作りますが、
pegy

2020/04/01 04:12

感覚的にですが settingKey = "timer_value" の様に let color = "color_value"; の様にキーになるものに文字列を代入すことはしないです。あまり、10を呼び出すためのキーはあくまでcolor.なのでとても不思議です。 上記のSwiftのコードでもsettingKey:10が成り立っているのにあえて settingKey = "timer_value" としてあげるところが不思議なのです。 コンパイル型特有の記法であれば勉強不足かもしれません。 細かい点で本当に申し訳ございません。
MasakiHori

2020/04/01 05:06

なるほどわかりました。 あなたは定数というものを正しく理解できていません。 これは辞書の話と似た話で、定数は値にラベルをつけたものです。 ラベルだけでは意味を成しません settingKeyの部分はすべて"timer_value"のことです let settingKey = "timer_value" としたことで本来 settings.register(defaults: ["timer_value":10]) と書かなければならないものが settings.register(defaults: [settingKey:10]) と書けるということです。 他でsettingKeyを使っている部分も同様です。 typoのバグというのは、定数名のsettingKeyのスペルを間違えてしまっていてもコンパイラがエラーを発生させるので間違いに気づけますが、文字列内 timer_value のスペルを間違えていてもコンパイラはエラーを発生させない。つまりtypo/スペルミスでバグが発生してしまうということです。
MasakiHori

2020/04/01 05:36

typoの可能性がある文字列を何度も書くのをやめて、typoがあってもコンパイラによって間違いが気付ける定数を利用しているということです
pegy

2020/04/01 06:24

なるほど、大変よくわかりました。 逆に色々と混乱させてしまい、申し訳ございません。こころからお詫び申し上げます。 コンパイル言語では一般的な書き方(定数を使用することが)なのかもしれませんが、これに慣れていこうと思います。また、これからコードを書き進める中でなるほどと思える局面が出てくるとも思いますので、ご質問できてよかったです。 最後までお使いをいただいたことについて、改めてお詫びと御礼を申し上げます。 よろしくお願い申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問