RORK LABEN
MAX — Rork MaxはReact NativeではなくネイティブSwiftアプリを生成し、iPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageまでを対象にしますAPIS — SwiftUI・ARKit・HealthKit・HomeKit・Core ML・MetalといったネイティブAPIに手が届き、AR/LiDARスキャンやオンデバイス機械学習も扱えますCLOUD — Swiftコードを書き、クラウド上のMacでコンパイルし、実機タッチ入力に対応したライブシミュレータをストリーミングしますSUBMIT — 実機インストール用、あるいはApp Store提出用のビルドまで用意されますSEED — Rorkは4月にLeft Lane Capital主導の$15Mシードを調達しました。Peak XVとa16z Speedrunも参加していますPRICE — 無料で始められ(週5プロンプト前後)、有料プランは$25/月から。Rork Maxは$200/月ですMAX — Rork MaxはReact NativeではなくネイティブSwiftアプリを生成し、iPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageまでを対象にしますAPIS — SwiftUI・ARKit・HealthKit・HomeKit・Core ML・MetalといったネイティブAPIに手が届き、AR/LiDARスキャンやオンデバイス機械学習も扱えますCLOUD — Swiftコードを書き、クラウド上のMacでコンパイルし、実機タッチ入力に対応したライブシミュレータをストリーミングしますSUBMIT — 実機インストール用、あるいはApp Store提出用のビルドまで用意されますSEED — Rorkは4月にLeft Lane Capital主導の$15Mシードを調達しました。Peak XVとa16z Speedrunも参加していますPRICE — 無料で始められ(週5プロンプト前後)、有料プランは$25/月から。Rork Maxは$200/月です
記事一覧/アプリ開発
アプリ開発/2026-07-16上級

壁紙の段組みギャラリーにネイティブ広告を差し込む — 広告セルの寿命をプールで設計する

段組みギャラリーにネイティブ広告を1枚入れたところ、20分のスクロールでメモリが伸び続けました。リスト再利用と広告の寿命が噛み合わない理由から、プール方式の実装、差し込み間隔の決め方まで実測とともに残します。

Rork511Expo144AdMob70ネイティブ広告FlashList5

プレミアム記事

段組みのギャラリーに、ネイティブ広告を1枚だけ差し込みました。10枚目のあとに1枚。それだけの変更です。

翌朝、Xcode の Memory Report を開いたまま20分ほどスクロールを続けたところ、メモリが 180 MB から 420 MB まで伸びて戻りませんでした。広告を外すと 190 MB 前後で平らになります。原因は広告そのものではなく、広告を「セルの一部」として扱っていた私の設計にありました。

個人開発で壁紙アプリを何本か回していると、広告の実装は毎回どこかで手が止まります。今回つまずいたのは、リストの再利用という React Native 側の都合と、ネイティブ広告オブジェクトの寿命という広告 SDK 側の都合が、まったく違う時間軸で動いていたことでした。

その噛み合わなさをどう設計で吸収したかを残します。段組みギャラリーそのものの組み方は高さの異なる画像を2列で美しく並べる—Rork の Expo アプリで壁紙ギャラリーを段組みにするに書きましたので、この記事はその上に広告を載せる話に絞ります。

段組みに広告を1枚入れた翌日に起きたこと

症状は3つ、同時に出ました。

症状広告なし広告あり(素朴な実装)
20分スクロール後のメモリ(iPhone 14, 実機)190 MB 前後で平ら420 MB まで単調増加
スクロール中のフレーム落ち(60fps 基準・1分あたり)2〜4 回28〜41 回
広告の表示回数 / スクロール到達回数0.62(4割が空白セル)

3つとも別の問題に見えて、根は1つでした。広告を返す関数を renderItem の中で呼んでいたのです。

// 素朴な実装。ここから3つの症状が全部出ました
function renderItem({ item }: { item: GridItem }) {
  if (item.type === 'ad') {
    return <NativeAdCell unitId={AD_UNIT_ID} />; // ← セルの中で読み込む
  }
  return <WallpaperCell wallpaper={item.wallpaper} />;
}

NativeAdCell は自分の useEffect の中で広告を読み込みます。素直に見えます。けれど FlashList も FlatList も、画面外に出たセルを破棄せず、別のデータで使い回します。つまりこのコンポーネントは、スクロールのたびにマウントとアンマウントを繰り返します。

そのたびに新しい広告リクエストが飛び、そのたびに新しいネイティブ広告オブジェクトが生まれます。そして誰も捨てません。

広告オブジェクトの寿命はセルの寿命と一致しない

ここが一番の勘所でした。

React のコンポーネントは「画面に映っている間だけ生きていればよい」という前提で書きます。アンマウントされたら消える、と私たちは思っています。

ところがネイティブ広告は違います。GADNativeAd(iOS)や NativeAd(Android)は JavaScript 側の参照が切れてもネイティブ側に残ります。明示的に destroy() を呼ぶまで、画像アセットもクリック計測用のビューも保持され続けます。JavaScript の GC はネイティブヒープを掃除してくれません。

さらに厄介なのは、広告の読み込みが 400〜900 ms かかることです。セルが画面外に出るのに要する時間より長い。素早くスクロールすると、こういう順序になります。

  1. セルがマウントされ、広告リクエストを送る
  2. 320 ms 後、セルが画面外に出てアンマウントされる
  3. 610 ms 後、広告が返ってくる — 貼り付ける先のビューはもう無い
  4. 広告オブジェクトはネイティブ側に残る。誰も destroy() を呼ばない

先ほどの「表示回数 / 到達回数 = 0.62」は、この 3 の状態が4割起きていたということです。読み込んだのに一度も見られていない広告が、メモリだけ食っていました。

私はこれを「寿命のズレ」と呼んで区別するようにしました。セルの寿命は数百 ms、広告の寿命は分単位。この2つを同じライフサイクルに縛ろうとすると、必ずどちらかが壊れます。

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

この記事の続きを読む

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

この記事で得られること
リストのセル再利用と広告オブジェクトの寿命が噛み合わず、メモリが伸び続ける仕組みと切り分け手順
広告をプールで保持しセルには貸すだけにする実コード、および破棄タイミングの判断ライン
差し込み間隔・スクロール中の読み込み抑止・段組みの高さ計算を、実測値をもとに決める方法
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

アプリ開発2026-07-07
高さの異なる画像を2列で美しく並べる—Rork の Expo アプリで壁紙ギャラリーを段組みにする
縦横比のばらつく画像を numColumns では美しく並べられない理由から、列の高さを見て振り分ける段組みアルゴリズム、FlashList の masonry での仮想化、依存を増やさない実装まで、壁紙ギャラリーを実コードで組みます。
アプリ開発2026-07-14
壁紙を長押しして、プレビューと保存メニューを出す — Rork の Expo アプリで context menu を組む
壁紙ギャラリーのカードを長押ししても何も起きない。iOS利用者はプレビュー付きのメニューを期待します。Pressableだけでは届かない理由から、zeegoでのネイティブcontext menu実装、スクロールとの競合解消、保存・共有アクション、Android向けの自前フォールバックまで、動くコードでまとめます。
アプリ開発2026-07-05
ワンタイムコードの入力欄をExpoで作る — SMS自動入力とセグメント表示を両立させる
6桁の確認コードを入れる画面は、見た目こそ単純ですが、SMSの自動入力・貼り付け・1文字ずつの削除まで含めると意外と作り込みが必要です。iOSとAndroidの自動入力を最初に押さえ、単一のTextInputで崩れにくいセグメント表示を組む実装をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →