RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-05-25上級

Rork iOSアプリのメモリプレッシャー対策 — 壁紙アプリ運用で磨いた5段階解放アーキテクチャ

Rork経由でビルドしたiOSアプリのメモリプレッシャー対策を、5段階解放という考え方で整理しました。壁紙アプリ群を12年運用してきた中で見えた優先順位と、Instrumentsでの計測手順を実装込みで紹介します。

Rork515iOS108メモリ管理3OOMパフォーマンス30Instruments3壁紙アプリ28個人開発187

プレミアム記事

朝のメールで Crashlytics の通知を開いたとき、また同じパターンが上に来ていました。「Out of Memory (Background)」の系統で、再現端末は iPhone SE 第3世代と iPad mini 6。前者は物理メモリ 4GB、後者は 4GB ですが iPadOS のマルチタスクが厳しめで、メモリ警告から数秒で OS にプロセスを停止されるケースが続いていました。

壁紙アプリを運用していると、メモリの問題は避けて通れません。ユーザーは「次の壁紙」「次の壁紙」と高解像度の画像を切り替え続けます。3,000 x 5,000 ピクセル前後のオリジナル素材を扱う場合、デコード済みで 1 枚あたり 60MB を超える領域を確保することになり、5 〜 6 枚をリスト表示するだけで物理メモリの肩を叩く水準に達します。

2014 年から個人で iOS / Android アプリを作り続けてきました。累計ダウンロードは 5,000 万を超え、その多くが壁紙・癒し・引き寄せ系の画像中心のアプリです。Rork 経由で React Native ベースに乗せ替えた最近のラインナップでも、結局この「画像が重い」というメモリ特性は変わりません。むしろ JS ブリッジを挟む分、ネイティブと連携した解放戦略を持っておかないと、メモリ警告が来てから手当が間に合わないという場面が増えました。

ここ 12 年で磨いてきたのが「5 段階解放アーキテクチャ」という設計です。AdMob 経由の広告収益を月 100 万円超まで育てる過程で、OOM によるクラッシュは LTV を直接削る一番痛い指標でした。Rork が出力する Expo ベースのプロジェクトで実際に使っている実装を、Swift と TypeScript の両側からまとめます。OOM 率 4.3% から 0.36% まで落ちた計測フローも、Instruments と MetricKit の併用方法を含めて共有します。アーティスト・個人開発者の廣川政樹として、App Store / Google Play の両方で 12 年運用してきた中で見えてきた現場感を、できる限りそのまま残しました。

なぜ「段階解放」が必要か

iOS のメモリ警告は二段階で来ます。まず軽い warning が UIApplication.didReceiveMemoryWarning 通知として配信され、無視していると数秒以内に jetsam(バックグラウンドの場合はさらに厳しい閾値)でプロセスごと殺されます。フォアグラウンドでも、警告から復帰せずに確保を続けると applicationWillTerminate も呼ばれないまま落ちます。

実装の素朴な対応は「警告が来たら画像キャッシュを全部クリア」です。これでも OOM 率は下がりますが、副作用としてユーザー体験が大きく劣化します。スクロール中に警告が走った瞬間、画面上の画像までいったん消えてフラッシュが起き、再ロードのスピナーが出ます。壁紙アプリの場合、これは「アプリ全体が止まったように見える」レベルの違和感です。

そこで導入したのが、解放対象を 5 段階に分けて、警告のレベルや残メモリの逼迫度に応じて段階的に手放す設計です。各段階はユーザー体験への影響度の昇順で並んでいて、本当に逼迫してきたときだけ深い段階まで触ります。

  1. Tier 1: ディスクキャッシュとプリフェッチ済みリモートデータ — 失っても再取得できる、ユーザーから不可視のもの
  2. Tier 2: 非可視ビューのデコード済みビットマップ — 画面外の画像キャッシュ
  3. Tier 3: アニメーション・トランジション用の中間バッファ — 体感に直結するが復元可能なもの
  4. Tier 4: 可視範囲外のサムネイル・近接プリフェッチ — スクロール時の体感を支えているが捨てれば再構築可能
  5. Tier 5: 現在見えている画像の高解像度バリアント — 最終手段。低解像度プレースホルダーに置換

順番に手放すことで、ユーザーが体感する劣化を最小化しつつ、jetsam による強制停止の確率を大きく下げられます。私のアプリ群では、この設計を入れる前後で OOM 率が 4.3% → 0.36% に下がりました(直近 30 日 / iPhone SE 第3世代)。

ネイティブ側で受け取る — Swift の Memory Pressure Observer

Rork から生成された Expo プロジェクトでも、ネイティブモジュールを 1 つ追加すれば iOS のメモリ警告を React Native 側に流せます。まずは Swift 側の受け口です。

import Foundation
import UIKit
import os
 
/// 5段階のメモリ解放を担当するシングルトン
@objc(MemoryPressureManager)
class MemoryPressureManager: NSObject {
 
    enum Tier: Int {
        case disk = 1       // Tier 1: ディスクキャッシュ
        case offscreen = 2  // Tier 2: 非可視ビュー
        case animation = 3  // Tier 3: アニメーションバッファ
        case adjacent = 4   // Tier 4: 近接プリフェッチ
        case visibleHi = 5  // Tier 5: 可視範囲の高解像度
    }
 
    static let shared = MemoryPressureManager()
    private let log = Logger(subsystem: "design.dolice.memory", category: "pressure")
    private var lastReleasedTier: Tier = .disk
    private var lastReleasedAt: Date = .distantPast
 
    override init() {
        super.init()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(didReceiveMemoryWarning),
            name: UIApplication.didReceiveMemoryWarningNotification,
            object: nil
        )
    }
 
    @objc private func didReceiveMemoryWarning() {
        let footprint = currentFootprintMB()
        log.warning("memory warning at \(footprint, format: .fixed(precision: 1)) MB")
        escalateRelease(currentFootprintMB: footprint)
    }
 
    /// 残メモリ・直近の解放履歴を見て次に解放する Tier を決める
    private func escalateRelease(currentFootprintMB: Double) {
        let now = Date()
        let secondsSinceLast = now.timeIntervalSince(lastReleasedAt)
 
        // 直近 3 秒以内に同じ Tier を解放したばかりなら、もう 1 段深く
        let nextRaw: Int
        if secondsSinceLast < 3.0 {
            nextRaw = min(lastReleasedTier.rawValue + 1, Tier.visibleHi.rawValue)
        } else if currentFootprintMB > 600 {
            // 600MB 超えはいきなり Tier 3 から
            nextRaw = max(lastReleasedTier.rawValue, Tier.animation.rawValue)
        } else {
            nextRaw = Tier.disk.rawValue
        }
 
        let tier = Tier(rawValue: nextRaw) ?? .disk
        notifyJS(tier: tier, footprint: currentFootprintMB)
        lastReleasedTier = tier
        lastReleasedAt = now
    }
}

ポイントは escalateRelease で「直近に何 Tier を解放したか」と「現在の物理フットプリント」を同時に見ている点です。短時間に何度も警告が来るときは、毎回 Tier 1 から始めると追いつきません。逆に久しぶりの警告でいきなり Tier 5 まで触るとユーザー体験が壊れるので、時間経過によるリセットを入れています。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
iOSのdidReceiveMemoryWarningを5段階に分けて優先度別に解放する具体的な実装手順
Instruments AllocationsとMetricKitでOOM率を4.3%から0.36%まで下げた計測フロー
壁紙アプリで累計5,000万DLを支えてきた、Rork経由でも適用できる低圧縮優先順位の判断軸
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

開発ツール2026-05-18
Rork で作った iOS アプリを iPhone Air と 17 Pro シリーズに対応させる — 2026年の解像度ごとのレイアウト調整パターン
iPhone Air・17 Pro・17 Pro Max の新解像度で既存 Rork アプリのレイアウトが崩れる問題を、4本の iOS アプリを並行更新した経験から解説。解像度定数管理・Safe Area・壁紙フルスクリーン表示の具体的な実装パターンを示します。
開発ツール2026-07-10
クラッシュとして記録されない強制終了 — Rork アプリの JetsamEvent を読む
Crashlytics には何も出ていないのにレビューで「勝手に閉じる」と書かれる。その多くはメモリ超過による OS からの強制終了です。JetsamEvent レポートの読み方と、画像中心アプリのメモリ上限を実測から設計する手順をまとめます。
開発ツール2026-06-25
壁紙アプリの画像キャッシュが静かに膨らんでメモリで落ちる — Rork運用で効いた計測と上限設計の運用メモ
画像が主役のRorkアプリで、ディスクキャッシュとメモリ常駐が少しずつ膨らみ、OOMクラッシュとして表面化する問題への対処。実測フック・キャッシュ上限・配信側リサイズまで、運用で効いた順に実装コード付きで整理します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →