8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

体の芯からあったまるアプリケーション

Last updated at Posted at 2024-12-21

初めに

冬,寒いですよね.

あなたの手の中にある、スマートなフォン.略してスマホ.
スマートフォンはスマートなフォンなので, ホッカイロにだってなれるはずです.
作っていきましょう.


使い方

アプリを起動します.

あったかくなるのを待ちましょう.

あったかいiOSアプリ

材料を紹介します.

材料 個数
XCode 1個
ペラペラのView 1個
std::thread 論理コアに応じた数
いい感じの処理 (意味のない処理でもOK) 少々

処理をおこなうクラス

今回の目的はコア分いっぱいに処理を実行して体を温めることですので,GCDなど使う必要はありません.むしろ使いたくありません.そのため,C++のstd::threadで必要な数分スレッドを作ることにしました.

さてstd::threadを使うことにしましたが,2025年のアプリ開発では基本的にSwiftを用いると思います.
いままでは, SwiftからC++のクラスを呼び出すにはObj-Cでラップするなど少々めんどくさい実装が必要でした.
実は素晴らしいことに,最近SwiftからC++が呼べるようになったので,これを使っていきましょう.

namespace Warmer{
class Core{
private:
    std::vector<Process*> pi_;
    
    int thread_n_ = 1;
    int current_thread_ = 0;

public:
    Core(){
        thread_n_ = std::thread::hardware_concurrency();
    }
    
    ~Core(){}
            
    void start(){
        for(int i=0; i<thread_n_; i++){
            Process* calc = new Process();
            calc->run();
            pi_.push_back(calc);
        }
    }

    // ~~~~ 中略 ~~~~~
    
    void stop(){
        for(auto e : pi_){
            e->stop();
        }
        
        for(auto e : pi_){
            delete e;
        }
        
        pi_.clear();
    }      
};
};

std::threadを持つクラス内で,無限にこの___taskを呼びまくります.
少々の処理といいましたか,これでもかと言うほどのゴリゴリの処理を入れましょう.

とにかく和積の数を稼ぎます.pow関数なんかもいいですね.
コンパイラに不要と思われないように出力した値はちゃんと使い方が無難かもしれません.手抜きでprintしておきます.

ちなみに,本当に意味のない処理だと,電力が無駄ですし,CPUがかわいそうなのでモンテカルロ法で円周率も求めています.

void Warmer::Process::__task(uint64_t cycle, double x, double y){
    std::lock_guard<std::mutex> lock(mtx_);
    
    if(cycle == 0){
        pi_count_ = 0;
        return;
    }
    
    if (x*x + y*y <= 1.0) {
        pi_count_++;
    }

    double pi = ((double)pi_count_ / (double) cycle) * 4.0;
    pi_.store(pi, std::memory_order_release);
    
    
    // なんかよくわかんないけど和積を増やそう
    double sa = 0.0;
    double ca = 0.0;
    
    int N = N_;
    int M = M_;
    
    M_ = std::max(0, M_);
    M_ = std::min(M_, MAX_M);
    
    N_ = std::max(0, N_);
    N_ = std::min(N_, MAX_N);
    
    for(uint64_t i=0; i<N; i++){
        double cy = (double) N;
        
        double s = 0;
        double c = 0;
        
        for(int j=0; j<M; j++){
            s += std::sin((double) N / 1000.0 * std::pow(2.0, j) * 1.0 * pi) * ((double)i / cy) / (double) M;
            c += std::cos((double) N / 1000.0 * std::pow(2.0, j) * 1.0 * pi) * ((double)i / cy) / (double) M;
        }                                    
                
        sa += s / cy;
        ca += c / cy;
    }
            
    dummy_ += sa + ca;
    
    if(cycle % 1000000 == 0){
        printf("%f\n", dummy_);
    }
}

ペラペラのView

適当なのはご了承ください.
むしろ適当な方がいいですね.

温度のステートを細かく知ることはできませんが,定性的な状態を読み取ることは可能です.
ProcessInfo.processInfo.thermalStateに状態が入っていますので,これを読み取って表示させましょう.

class ContentModel: ObservableObject{
    enum State{
        case normal
        case fair
        case serious
        case critical
        
        var color: Color{
            switch self{
                case .normal:
                    return .blue
                case .fair:
                    return .green
                case .serious:
                    return .yellow
                case .critical:
                    return .red
            }
        }
        
        var label: String{
            switch self{
                case .normal:
                    return "冷たいね"
                case .fair:
                    return "ちょっとあったかい"
                case .serious:
                    return "あったかい〜"
                case .critical:
                    return "あちち"
            }
        }
        
        init(_ state: ProcessInfo.ThermalState){
            switch state {
                case .nominal:
                    self = .normal
                case .fair:
                    self = .fair
                case .serious:
                    self = .serious
                case .critical:
                    self = .critical
                @unknown default:
                    self = .normal
            }
        }
    }
    
    
    
    @Published var scale: Double = 1.0
    @Published var boost: Double = 0.0
    @Published var level: Double = 0.0
    @Published var thermalState: State = .normal
    
    var core: Warmer.Core
    
    init(){
        self.core = Warmer.Core()
        self.core.start();
        var timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in
            self.scale = self.core.pi() / 3.14159265358979
            self.thermalState = .init(ProcessInfo.processInfo.thermalState)
        })
    }
    
    func updateBoost(_ value: Double) {
        core.boost(value)
        objectWillChange.send()
    }
    
    func updateLevel(_ value: Double) {
        core.level(value)
        objectWillChange.send()
    }
}
struct ContentView: View {
    
    @ObservedObject var viewModel: ContentModel
    
    init(){
      viewModel = ContentModel()
    }
        
    var body: some View {
        VStack{
            Spacer()
            HStack{
                Spacer()
                ZStack{
                    Circle()
                        .foregroundStyle(viewModel.thermalState.color)
                        .frame(width: 300, height: 300)
                    Circle()
                        .stroke(Color.blue, lineWidth: 3)
                        .frame(width: 300, height: 300)
                        .scaleEffect(viewModel.scale)
                    Text(viewModel.thermalState.label)
                        .foregroundStyle(Color.white)
                        .font(.system(size: 18, weight: .bold))
                    
                }
                Spacer()
            }
            Spacer()
            
            HStack{
                Text("Boost")
                Slider(value: $viewModel.boost, in: 0...1, step: 0.1){ newValue in
                    viewModel.updateBoost(viewModel.boost)
                }
            }
            
            HStack{
                Text("Level")
                Slider(value: $viewModel.level, in: 0...1, step: 0.1){_ in
                    viewModel.updateLevel(viewModel.level)
                }
            }
        }
        .padding()
    }
}

XCodeで10秒焼き上げます

あなたのXCodeに入れて、10秒ほど焼き(コンパイルし)ます.

完成!

あとは,立ち上げてまつだけ!

けっこう暖かくなります.
温度計がないので,詳細な温度はわかりませんが ;-(

最後に

これで寒い冬も乗り切れます.

バッテリーが1分で1%減りますが...
(iPhone SE 3rd)

免責事項

バッテリーの劣化を引き起こすため,試さないでください.
試してしまっても,記事の内容にによt生じるいかなる損害についても,私は責任を負いかねます....!

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?