RORK LABEN
PLAY — Google Play の target API level 36 要件が昨日8月31日に発効しました。今日以降、新規アプリと既存アプリの更新は Android 16 対応が必須ですVISIBILITY — API 35 のままのアプリは掲載こそ続きますが、新しい Android 版のユーザーには表示されなくなります。エラーが出ないまま新規インストールだけが減る点に注意が要りますEXTENSION — 間に合わなかった場合は、Play Console から2026年11月1日までの延長申請が出せます。恒久対応の計画とセットで進めるのが実務的ですAPPLE — Apple 側は9月9日にイベント、iOS 27 の正式リリースは9月14日と報じられています。生成したアプリの iOS 27 実機確認はリリース週の前に済ませておきたいところですEXPO — Expo が expo-paste-input を公開しました(8月28日)。React Native の TextInput に画像・GIF・ステッカーの貼り付けを追加するネイティブモジュールですEAS — EAS Observe が8月20日に GA になりました。クラッシュや性能の観測を、ビルドや配信と同じ EAS 上で持てるようになっていますPLAY — Google Play の target API level 36 要件が昨日8月31日に発効しました。今日以降、新規アプリと既存アプリの更新は Android 16 対応が必須ですVISIBILITY — API 35 のままのアプリは掲載こそ続きますが、新しい Android 版のユーザーには表示されなくなります。エラーが出ないまま新規インストールだけが減る点に注意が要りますEXTENSION — 間に合わなかった場合は、Play Console から2026年11月1日までの延長申請が出せます。恒久対応の計画とセットで進めるのが実務的ですAPPLE — Apple 側は9月9日にイベント、iOS 27 の正式リリースは9月14日と報じられています。生成したアプリの iOS 27 実機確認はリリース週の前に済ませておきたいところですEXPO — Expo が expo-paste-input を公開しました(8月28日)。React Native の TextInput に画像・GIF・ステッカーの貼り付けを追加するネイティブモジュールですEAS — EAS Observe が8月20日に GA になりました。クラッシュや性能の観測を、ビルドや配信と同じ EAS 上で持てるようになっています
記事一覧/アプリ開発
アプリ開発/2026-07-16上級

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

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

Rork547Expo192AdMob70ネイティブ広告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/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-07-07
高さの異なる画像を2列で美しく並べる—Rork の Expo アプリで壁紙ギャラリーを段組みにする
縦横比のばらつく画像を numColumns では美しく並べられない理由から、列の高さを見て振り分ける段組みアルゴリズム、FlashList の masonry での仮想化、依存を増やさない実装まで、壁紙ギャラリーを実コードで組みます。
アプリ開発2026-08-23
targetSdk を 36 に上げたら 16 KB の警告が出ました。この2つは別の期限です
対象 API レベル36 の8月31日と、16 KB メモリページサイズの2027年2月1日は別の条件です。NDK を入れずに手元の AAB を検査する方法と、LOAD のずれと zip 境界のずれで直し方が変わることを、実際の出力とあわせて整理しました。
アプリ開発2026-08-21
Apple の自動解析は、purpose string が書いてあるかではなく何に使うかを読んでいます
Rork や Expo で生成した iOS アプリを提出すると、Guideline 5.1.1 で「placeholder or otherwise insufficient purpose strings」と返されることがあります。キー名が示されないこの指摘の読み方と、app.json から文言を書き直すまでの手順をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →