.NET MAUI/Xamarin.iOS で iOS の静的ライブラリ(.a)を NativeReference として組み込むとき、「IsCxx を true にすべきか?」で悩むケースは少なくありません。ビルドも実行も問題ないのに、本当に必要なのか分からない――この記事では、IsCxx の正体と、実務でどう判断すべきかを、できるだけ具体的に整理します。
iOS の静的ライブラリと NativeReference の全体像
まずは前提として、.NET MAUI / Xamarin.iOS における iOS ネイティブライブラリの扱いをざっくり整理しておきます。
.a(静的ライブラリ)とは何か
iOS の .a ファイルは、複数のオブジェクトファイルをまとめた「静的ライブラリ」です。アプリをビルドするときにリンカが .a を読み込み、必要なシンボルだけを最終バイナリ(App 本体)に取り込みます。
この .a の中身は、次のどれか(または混在)になっていることが多いです。
- C 言語だけで書かれている
- Objective‑C(+C)で書かれている
- C++ を含む(
std::やテンプレート、例外など)
.NET MAUI / Xamarin.iOS では、これらの .a を NativeReference(または古い Xamarin では [LinkWith] 属性)としてプロジェクトに追加してリンクします。
NativeReference と LinkWith の関係
.NET 6 以降の iOS / MAUI プロジェクトでは、通常 .csproj に次のような形で静的ライブラリを指定します。
<ItemGroup>
<NativeReference Include="..\libraries\libxxxx.a">
<Kind>Static</Kind>
<ForceLoad>True</ForceLoad>
<IsCxx>False</IsCxx>
<SmartLink>False</SmartLink>
</NativeReference>
</ItemGroup>
古い Xamarin.iOS のバインディングプロジェクトでは、同じ情報を LinkWithAttribute で記述していました。公式ドキュメントでは IsCxx プロパティについて「ネイティブライブラリが C++ ライブラリかどうかを指定する」と説明されています。
つまり NativeReference の IsCxx は、LinkWithAttribute の IsCxx と同じ意味を持つプロパティで、どちらも「そのライブラリを C++ として扱うべきかどうか」をツールチェーンに伝えるフラグです。
| 仕組み | 主な用途 | IsCxx の有無 |
|---|---|---|
| NativeReference(.csproj) | .NET 6+ / MAUI / .NET for iOS でのネイティブライブラリ参照 | <IsCxx>True/False</IsCxx> |
[LinkWith] 属性 | 旧 Xamarin.iOS のバインディングプロジェクト | IsCxx = true/false |
IsCxx とは?公式情報から読み解く役割
では、IsCxx を true にすると何が起きるのでしょうか。
公式のバインディングリファレンスでは、IsCxx プロパティについてざっくり次のように説明されています。
- 結果の実行ファイルを C++ ランタイムとリンクする必要がある場合に
trueに設定する - このプロパティは、C++ で書かれたライブラリをバインドするときに使用する
また LinkWithAttribute の API ドキュメントは、IsCxx を「ネイティブライブラリが C++ ライブラリかどうかを指定する」プロパティと説明しています。
さらに、Microsoft Q&A のスレッドでは、C++ で書かれた iOS ネイティブライブラリは追加の初期化処理を必要とすることがあり、IsCxx を有効にするとその初期化コードが挿入される、という趣旨の回答が紹介されています。
これらをまとめると、IsCxx は「このライブラリは C++ を含むので、C++ ランタイムと初期化処理をきちんと行ってください」という合図だと考えるのが実務的です。
IsCxx が行うこと(概念的なイメージ)
内部実装は公開されていませんが、公式ドキュメントやエラー解説から推測できる範囲で、IsCxx が関与するポイントは次のようなものです。
- ビルド時に C++ ライブラリとして扱い、必要な C++ ランタイム(
libc++など)をリンクさせる - C++ のグローバル/静的オブジェクトのコンストラクタが正しく実行されるよう、初期化コードを組み込む
- C++ 例外や RTTI(ランタイム型情報)に関連するシンボルが解決されるようにする
実際、Xamarin.iOS のトラブルシューティングドキュメントでは、C++ で書かれたライブラリをバインドしたのに IsCxx を指定していないと、__ZNKSt9exception4whatEv などの C++ マングル名を含む未解決シンボルが出るケースを紹介し、[LinkWith ("mylib.a", IsCxx = true)] とすることで解消できると説明しています。
つまり IsCxx は最適化フラグではなく、「C++ らしい振る舞いを保証するための互換フラグ」だと理解しておくのがポイントです。
IsCxx を切り替えても挙動が変わらない理由
質問のように、IsCxx を true/false で切り替えても、
- ビルドが通る
- 実行も問題ない
- アプリサイズもほぼ変化しない
という状況になるのは珍しくありません。主な理由は次のいずれかです。
理由1:ライブラリに C++ コードが含まれていない
もっとも多いのがこのパターンです。
.aが純粋な C/Objective‑C で書かれている- ヘッダを見ても
classやstd::、テンプレートなどが出てこない
この場合、C++ ランタイムをリンクする必要がそもそもありません。IsCxx を true にしても追加でリンクされるものがほとんどなく、結果として挙動もサイズも変わらない、ということになります。
理由2:他のライブラリですでに C++ 依存が解決されている
アプリが複数のネイティブライブラリをリンクしている場合、
- 別の NativeReference が C++ ライブラリで、その時点で C++ ランタイムがリンク済み
- そのため、問題の
.aに対する IsCxx のオン/オフでは、リンク結果がほとんど変わらない
ということもあります。特に gRPC、ONNX Runtime、MLKit など、大きめの C++ ライブラリを複数組み合わせているプロジェクトではありがちなパターンです。
理由3:リンカが未使用コードを落としている
iOS のリンカや .NET の AOT コンパイラは、未使用のシンボルを積極的に削除します。
- ライブラリ内の C++ 由来のコードが実際には呼ばれていない
- あるいは IsCxx によって追加でリンクされても未参照なので削除される
といった場合、IsCxx を切り替えても結果のバイナリサイズはほとんど変化しません。
パフォーマンス・アプリサイズへの影響は?
IsCxx はパフォーマンスチューニング用のフラグではありません。そのため、IsCxx を true にしたからと言ってコードの実行速度が速くなることは基本的にありません。
サイズについても、差が出るのは「C++ ランタイムや標準ライブラリを新たにリンクする必要があるとき」だけです。その場合でも、数百 KB~数 MB 程度の増加に収まることが多く、アプリ全体から見ると「わずかな差」であるケースがほとんどです(もちろん、サイズを極限まで追い込みたい特殊なアプリでは無視できない場合もあります)。
| ケース | IsCxx=false | IsCxx=true | 想定される影響 |
|---|---|---|---|
| ライブラリが C / ObjC のみ | 正常にリンク・実行 | ほぼ同じ結果 | サイズ差・性能差ともほぼゼロ |
| ライブラリが C++ を含むが、他ライブラリが既に C++ 依存を満たしている | 運良く(?)動くこともあるが、将来の変更で壊れるリスク | より安全な設定 | サイズ差はごく小さいことが多い |
| ライブラリが C++ を含み、他に C++ 依存はない | 未解決シンボルやクラッシュの可能性 | 正しくリンク・実行される | C++ ランタイム分だけバイナリが増える可能性 |
ライブラリが C++ を含むかどうかの見分け方
IsCxx をどうするか判断するには、「そのライブラリが C++ を含んでいるか」を知る必要があります。実務的に使いやすい判定方法をいくつか紹介します。
1. ベンダーのドキュメント・ヘッダで確認する
もっとも確実なのは、ライブラリを提供しているベンダーのドキュメントやヘッダファイルを確認する方法です。
- 「C++ ライブラリ」「C++11 が必要」「libc++ へのリンクが必要」といった記述がある
- ヘッダで
class、namespace、template<T>やstd::stringなどの記述が出てくる
こうした場合は、ほぼ確実に C++ が使われています。IsCxx は true にしておくべきです。
2. nm コマンドでシンボルを確認する
実機の Mac が触れるなら、ターミナルで nm コマンドを叩くのが手っ取り早いです。
# ライブラリが C++ を含んでいそうかざっくり調べる
nm -j libxxxx.a | grep '^_Z'
# C++ 例外や ABI 関連のシンボルを探す
nm -j libxxxx.a | grep '__cxa'
# 標準ライブラリの利用有無を探す
nm -j libxxxx.a | grep 'std::'
_Zで始まるシンボルは Itanium 形式の C++ マングル名であることが多い__cxa_throwや__cxa_allocate_exceptionなどの__cxa*シンボルは C++ 例外処理の実装に関するものstd::basic_string<...>等が見つかれば C++ 標準ライブラリ依存がある
これらが多数見つかる場合、その .a は C++ ライブラリとして扱うべきと考えてよいでしょう。
3. ビルドエラーの内容から逆推定する
IsCxx を false のままビルドして、次のようなリンカエラーが出る場合も、C++ 依存のサインです。
Undefined symbols for architecture arm64: "__ZNKSt9exception4whatEv", referenced from ...Undefined symbols for architecture arm64: "___cxa_throw", referenced from ...Undefined symbols for architecture arm64: "std::basic_string<...>::basic_string(...)"
この手のエラーは公式の mtouch エラー解説でも「C++ ライブラリを C++ として扱っていない」典型例として挙げられており、解決策として IsCxx を有効にすることが案内されています。
IsCxx を true にしないとどうなる?
ライブラリが C++ を含むにもかかわらず IsCxx を false のままにしていると、次のような問題が発生する可能性があります。
1. ネイティブリンクエラー(ビルドエラー)
最も分かりやすいのは「ネイティブリンクエラーでビルドが通らない」パターンです。
MT5210やMT5202などの mtouch エラー- C++ 標準ライブラリや
__cxa*シンボルが未解決として報告される
この場合、IsCxx を true にしたうえで、必要に応じて libc++ をリンクするよう LinkerFlags を調整することで解決することが多いです。
2. 実行時におかしな挙動をする
さらに厄介なのが、「ビルドは通るが、実行時に妙な挙動をする」ケースです。
- C++ のグローバルオブジェクトや static 変数が初期化されていない
- シングルトンパターンや内部キャッシュがうまく機能しない
- 例外ハンドリングが期待通りに動かず、突然アプリが落ちる
こうした現象は、C++ ライブラリ特有の初期化処理が正しく動いていないときに起きがちです。IsCxx を true にして再ビルドしたところ問題が解消した、という報告もいくつか見られます。
IsCxx の実務的な設定方針(ベストプラクティス)
ここまでを踏まえて、現場での判断に使えるシンプルな指針をまとめます。
基本方針
- ライブラリが C / Objective‑C のみ → IsCxx は false のままでよい
- C++ を含むことが明らか → IsCxx を true にする
- 中身が不明で、IsCxx=false でもビルド・実行ともに安定 → そのまま false を使い続けてもよい
- リンクエラーや怪しい挙動がある → 試しに IsCxx=true でビルドしてみる
先述の Microsoft Q&A でも、C++ でないライブラリの場合は IsCxx を無効にしておくほうが無難、という趣旨のコメントがなされています。
IsCxx を true にしておいても安全か?
「ならいっそ、全部 IsCxx=true にしておけば安全では?」という発想もあるかもしれません。実際、多くの場合はそれでも問題ありませんが、次のようなデメリットも考えられます。
- 不要な C++ ランタイムがリンクされ、わずかながらサイズが増える可能性
- 将来的にツールチェーンの挙動が変わったとき、思わぬ副作用が出るリスク
とはいえ、最近のケースでは 「C++ を使う可能性がある中規模以上のネイティブライブラリには、最初から IsCxx=true を付けておく」という運用も現実的です。最終的には、
- プロジェクトのサイズ要件(数 MB の差も許されないか)
- ライブラリの性質(完全ブラックボックスか、自分たちでビルドしているか)
といった要因を踏まえて、チームポリシーとして決めるのが良いでしょう。
.NET MAUI / Xamarin.iOS での具体的な設定例
.NET MAUI の .csproj で C++ 静的ライブラリをリンクする例
簡単な C++ ライブラリ libadd.a を .NET MAUI iOS プロジェクトから呼び出す例を考えてみます。元の C++ コードは次のようなものとします。
// add.cpp
extern "C" int add(int a, int b)
{
return a + b;
}
これを Xcode などで libadd.a にビルドしたとします。MAUI プロジェクトの .csproj には次のように記述します。
<ItemGroup>
<NativeReference Include="Platforms/iOS/libadd.a">
<Kind>Static</Kind>
<ForceLoad>True</ForceLoad>
<IsCxx>True</IsCxx>
<SmartLink>True</SmartLink>
</NativeReference>
</ItemGroup>
そして C# コード側では、次のように P/Invoke します。
using System.Runtime.InteropServices;
internal static class NativeMethods
{
[DllImport("__Internal", EntryPoint = "add")]
internal static extern int Add(int a, int b);
}
UI コードから NativeMethods.Add(1, 2) を呼び出せば、C++ 側の実装が実行されます。
旧 Xamarin.iOS バインディングでの LinkWith 例
古い Xamarin.iOS バインディングプロジェクトでは、同様のことを [LinkWith] 属性で指定していました。
using ObjCRuntime;
[assembly: LinkWith(
“libadd.a”,
IsCxx = true,
SmartLink = true,
ForceLoad = true)]
この LinkWith のメタデータは、.NET 6+ の世界では NativeReference の各プロパティにほぼ対応しています。
IsCxx 以外の関連プロパティとの関係
IsCxx と一緒に見かけることが多いプロパティも、実務上はよくセットで調整することになります。LinkWithAttribute の公式ドキュメントにある説明をベースに、ざっくり整理してみましょう。
| プロパティ | 役割 | サイズ/性能への影響 | 典型的な使いどころ |
|---|---|---|---|
IsCxx | ライブラリが C++ かどうかを指定し、C++ ランタイムや初期化コードを有効にする | C++ ランタイム分だけサイズ増加の可能性。性能への直接の影響はほぼなし | C++ で書かれた静的ライブラリをリンクするとき |
ForceLoad | リンカに -force_load を渡し、未参照に見えるシンボルも強制的に取り込む | 使われないコードまでリンクされるとサイズが増える可能性 | Objective‑C のカテゴリや C++ の静的初期化子が最適化で落とされて困るとき |
SmartLink | 未使用シンボルを積極的に削除するリンク動作を有効にする | サイズ削減に寄与するが、設定次第で必要なシンボルまで落ちるリスクも | 大きなライブラリをリンクするときのサイズ削減 |
NeedsGccExceptionHandling | 古い GCC 形式の例外処理ライブラリ(libgcc_eh)とのリンクが必要かどうか | 現代的な Clang/LLVM ベースのライブラリでは通常不要 | 非常に古い C++ ライブラリを扱う特殊ケース |
LinkWithSwiftSystemLibraries | Swift で書かれたライブラリに必要な Swift ランタイムを自動リンクする | Swift ランタイム分だけサイズが増える可能性 | Swift ベースの SDK やフレームワークをバインドするとき |
特に C++ ライブラリでは、
- IsCxx = true
- 必要に応じて ForceLoad = true
- サイズ削減したい場合は SmartLink = true だが、問題が出るようなら false に戻す
という組み合わせで調整することが多い印象です。
Swift / Objective‑C フレームワークとの違い
最近は Swift 製 SDK を .NET MAUI から呼び出したい、というケースも増えています。この場合、IsCxx に加えて Swift 用の設定(LinkWithSwiftSystemLibraries や Swift 標準ライブラリのリンク)が必要になることがあります。
ポイントとしては、
- Objective‑C と Swift はどちらも「C++ とは別系統」のランタイムを持つ
- Swift ライブラリに対して IsCxx を true にしても、Swift ランタイムは自動ではリンクされない
- Swift ライブラリには、専用の設定やヘルパー(Community Toolkit など)が用意されていることが多い
そのため、IsCxx はあくまで「C++ 向けのスイッチ」だと割り切り、Swift や純粋な Objective‑C ライブラリでは別の観点で設定を行う必要があります。
よくある疑問と答え
Q1. IsCxx を true にするとビルド時間は伸びる?
A. C++ ランタイムのリンクが追加される分、わずかにリンカの仕事が増える可能性はありますが、体感できるほどビルド時間が伸びるケースはあまりありません。ビルド時間への影響よりも、「C++ ライブラリを正しく動かすために必要かどうか」を優先して判断すべきです。
Q2. ライブラリの中身が分からない場合はどうする?
A. まずはヘッダやドキュメント、nm コマンドなどで C++ っぽい要素があるか確認します。それでも分からない場合、
- IsCxx=false でビルド・実行し、問題がないかを確認
- 問題が出るようなら IsCxx=true にして再チェック
という二段階アプローチがおすすめです。対照的に挙動を比べることで、IsCxx が関与しているかどうかを切り分けやすくなります。
Q3. IsCxx を切り替えてもサイズが変わらない。どちらにしておくべき?
A. そのライブラリが C++ を含むことが確認できているなら、多少のサイズ増加を許容して IsCxx=true にしておくほうが安全です。一方、C / Objective‑C のみであることが分かっているなら、IsCxx=false のままでかまいません。
まとめ
最後に、NativeReference の IsCxx について、本記事のポイントを整理します。
- IsCxx は「ネイティブライブラリが C++ を使っているかどうか」をツールチェーンに知らせるフラグであり、性能チューニング用ではない
- C++ ライブラリでは、C++ ランタイムとのリンクや静的オブジェクトの初期化が必要になるため、IsCxx=true が重要になる
- ライブラリが C / Objective‑C のみなら、IsCxx を切り替えても挙動やサイズは基本的に変わらない
- リンクエラー(
__cxa*やstd::系の未解決シンボル)や妙な実行時挙動がある場合、IsCxx=true を試してみる価値がある - 実務では、「C++ を含むライブラリだけ IsCxx=true」を基本方針にしつつ、nm コマンドやベンダードキュメントで中身を確認する
- ForceLoad / SmartLink / NeedsGccExceptionHandling などの関連プロパティもセットで理解しておくと、ネイティブ連携のトラブルシュートが格段に楽になる
.NET MAUI / Xamarin.iOS の iOS ネイティブ連携は、一度仕組みを押さえてしまえば非常に強力です。IsCxx をはじめとする NativeReference のプロパティを「なんとなく」ではなく「何をしているか」を理解しておくことで、将来の移行やライブラリの差し替えも安心して行えるようになるはずです。

コメント