LoginSignup

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Swift】関数の引数参照前後で値が変わる??

解決したいこと

Xcodeで定期的に処理を行うアプリをつくっています。
定期的に処理を行う中である値を参照する前後で値が変わってしまう症状が見られました。
解決方法を教えて下さい。

開発環境

Xcode Version 14.2
Interface Storyboard
macOS monterey 13.0.1

問題点

以下のコードを出力した結果、2箇所の'print(deltaDegree)'で異なる値が出力されました。
出力結果は以下のとおりです。

2023-02-02 04:25:18.089535+0900 ButtonSample[43127:1394882] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600003622e00> F8BB1C28-BAE8-11D6-9C31-00039315CD46
0.12195121951219512
117480667527541.67

一つ目の結果(0.12195121951219512)は、直前の計算から得られたもので、自前の電卓を用いて計算しても同様の結果が得られました。

該当するソースコード

import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    @IBOutlet weak var btnCount: UIButton!
    @IBOutlet weak var btnExplosion: UIButton!
    @IBOutlet weak var imgBom: UIImageView!
    
    // ボタンフラグ
    var buttonStartFlg = true
    // 現時点での回転角
    var currentDegree: CGFloat = 0
    // 定期割り込みのための Timer のインスタンス
    var timer = Timer()
    var interval: TimeInterval = 0.01
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func btnCount(_ sender: Any) {
        
        btnCount.isEnabled = false
        btnExplosion.isEnabled = true
        
        // 初期スピード
        let degree: CGFloat = 30
        // 回転処理に使われるメソッドは rotateImage)
        timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(rotateImage), userInfo: degree, repeats: true)
        buttonStartFlg = false
    }
    
    @IBAction func btnExplosion(_ sender: Any) {
        // 初期スピード
        let degree: CGFloat = 30

        let stopDegree: CGFloat = 90.0
        let dDmolecule = 0.5 * pow(degree, 2)
        currentDegree = 0
        let dDdenominator = (360.0 * 10) + stopDegree
        
        let deltaDegree = dDmolecule / dDdenominator

        print(deltaDegree)

        // 定期的な呼び出しを中止する
        timer.invalidate()
        // 回転を徐々に止めるためのメソッドを1回だけ(repeats: false)呼び出し
        // 止めるために使われるメソッドは stopImage
        timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(stopImage(_:deltaDegree:)), userInfo: degree, repeats: false)
    }

    @objc func rotateImage(_ sender: Timer) {
        let degree = sender.userInfo as! CGFloat
        let angle = currentDegree * CGFloat.pi / 180  // Radian
        let affine = CGAffineTransform(rotationAngle: angle)
        imgBom.transform = affine
        currentDegree += degree
        currentDegree.formRemainder(dividingBy: 360.0)
    }

    // ストップボタンを押した後、止まるまで呼び出される回転処理
    @objc func stopImage(_ sender: Timer , deltaDegree: CGFloat) {

        print(deltaDegree)

        var degree = sender.userInfo as! CGFloat        
        let angle = currentDegree * CGFloat.pi / 180  // Radian
        let affine = CGAffineTransform(rotationAngle: angle)
        
        imgBom.transform = affine
        
        currentDegree += degree
        currentDegree.formRemainder(dividingBy: 360.0)
        
        // 回転角を減らす
        degree -= deltaDegree
        
        if degree > 0 {
            // 回転角が正の間は、減らした回転角を使って再度タイマ割り込みを使う
            timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(stopImage(_:deltaDegree:)), userInfo: degree, repeats: false)
        }
    }
}

自分で試したこと

このコードは以下のサイトを参考に記述しました。

0

1Answer

引数に渡した変数が違うからではないでしょうか?

-          timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(stopImage(_:deltaDegree:)), userInfo: degree, repeats: false)
+          timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(stopImage(_:deltaDegree:)), userInfo: deltaDegree, repeats: false)
0

Comments

  1. @active_vintage

    Questioner
    回答ありがとうございます

    userInfoで送られる値は各関数内でsender.userInfoとして再度呼び出されるそうです。
    従って、nak435様にご指摘していただいた部分の変更は不要なのではないかと考えています。

    実際、ご指摘のとおり、変更してみても挙動は変わらず問題は解消しませんでした。

Your answer might help someone💌