swiftの入門書を写経時に少し混乱したので、自分用にメモ
間違ってるもしくは情報不足(確実にあると思う。)があったら都度修正予定。
つまづきかけた疑問点と簡易的回答
・変数・定数がそれぞれ複数の場所で宣言されている。
→スコープによるもの
・他の関数内で作られたインスタンスにおけるメソッドを外部の関数内の別インスタンスから扱えてしまっている。
→ 参照渡しによるもの → UserDefautlsによるもの
経緯
・viewDidLoad()内で定数インスタンスが宣言して作られている。
・そのインスタンスによって初期値のキーが登録されている。
・他関数内(viewDidLoad外)で同名の定数のインスタンスが作られている。
・その同名インスタンスではviewDidLoad()内で登録されたが、関数内で登録されていないキーを使用している。
ここで先の2つの疑問
1.定数は変更できないのにもかかわらず、複数宣言できているのはなぜか。
2.スコープの関係で複数の定数インスタンスが存在できるとして、どうして別のローカルスコープ(viewDidLoad関数)で設定されたキーを扱えるのか。
回答(調べた感じあってるとは思う。 → 間違っていました。)
1.関数・メソッド(スコープ)内で作られた変数や定数はローカル変数・定数として扱われ、スコープを抜けた時点で消えるため、そのスコープ外で同名の変数・定数が存在・宣言されていても問題はない。
2.ローカルスコープ内で作られてもクラスにおけるインスタンスは参照渡しであるため、インスタンスを通じて新たに設計されたものは参照元のデータにも影響を与えるため、スコープを抜け出てインスタンスが消えたあともスコープ内で設定された点は参照元に残る。
その参照元に新たに作られたインスタンスは参照元に残された部分も引き継ぐため扱うことができる。
ではなくて、
viewDidLoad()内でUserDefaultsによってインスタンスが生成されているため。これによりローカルスコープを抜けてもなお、データが保持される。
次の疑問点
それでは、前の仮設してた回答はどうだったのか。
もしもUserDefaultsを使用しないで通常の参照渡しによるインスタンス生成を行った場合では同じ結果にはならない?
インスタンスでの変更は参照元のデータに影響を与えることで、スコープを抜け出ても新たに同じデータを参照元にした別ローカルスコープ内のインスタンスはその影響を受けるれるとしたら同じような挙動になりそうだが・・・・
こちらに関しては調べてわかったら追記します。
以下ソース(写経時にあげたのでまだ途中しか書いてない。)
問題にしているのは settings の部分
class ViewController: UIViewController {
//set timer variable
var timer : Timer?
//set count
var count = 0
//set the key
let settingKey = "timer_value"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//create userdefaults instance
let settings = UserDefaults.standard
// initial date in UserDefaults
settings.register(defaults: [settingKey:10])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBOutlet weak var countDownLabel: UILabel!
@IBAction func settingButtonAction(_ sender: Any) {
}
@IBAction func startButtonAction(_ sender: Any) {
//unwrap timer & insert into nowtimer
if let nowTimer = timer {
//if started nostart
if nowTimer.isValid == true {
//no actiuon
return
}
}
//set timer
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector( self.timerInterrupt(_:) ), userInfo: nil, repeats: true )
}
@IBAction func stopButtonAction(_ sender: Any) {
//unwrap timer and into nowtimer
if let nowTimer = timer {
if nowTimer.isValid == true {
nowTimer.invalidate()
}
}
}
//update the UI ( return: remainCount:残り時間)
func displayUpdate() -> int {
//create userdefault instance
let settings = UserDefaults.standard
//give the sec totimerValue
let timerValue = settings.integer(forKey: settingKey)
//create remain time
let remainCount = timerValue - count
//display remainCount to UI
countDownLabel.text = "残り\(remainCount)秒"
//retun
return remainCount
}
// deal the past time
@objc func timerInterrupt(_ timer: Timer) {
//count ++
count += 1
//stop timer if remaincounter = 0
if displayUpdate() <= 0 {
count = 0
//stop timer
timer.invalidate()
}
}
}