Objective-C++を使ってMRCで快適に開発する | iOSDC 2017

iOSMacの開発において現在ではARC(Automatic Reference Counting)が前提となっており、MRCの時代と比べて飛躍的に簡単になりました。しかし、開発するアプリによってはMRCでメモリ管理をしたい場面が少なからずあるはずです。そんな時はObjective-C++の力を使って、ARCのように快適な開発をしましょう。

f:id:niwatako:20170915175621j:plain

ユビレジで働いています。

さて、Objective-C++でMRCを使うシチュエーションが果たしてあるのか?はおいておいてこのような話をさせていただきます。

メモリ管理の話と、そして、なぜC++かの話が見えてくると思います。

後半でサンプルコードが出ますが、スライドの下の方にURLがあります。スライドも公開すると思います。

Objective-Cのメモリ管理

MRCでないのはARCです。ObjCで参照カウント方式のメモリ管理をコンパイラがやってくれます。

f:id:niwatako:20170915175750j:plain

DefaultでYESになっています。

f:id:niwatako:20170915175816j:plain

NOにすれば使わないことができますがSwiftはARCだけです。

ARCを使わず自分でメモリを管理するのがMRCです。

f:id:niwatako:20170915175845j:plain

これがどのようなものか見ていきましょう。

f:id:niwatako:20170915175912j:plain

TestClass *value = [[TestClass alloc] init];
[value retain] // 参照カウント2
[value release] // 参照カウント1
[value release] // 参照カウント0

Autoreleaseと言うのもあります。

f:id:niwatako:20170915180016j:plain

[value autorelease] とすることで開放を予約することができ、 @autorelasepool{ } でかこまれた間を抜けると開放されます。

ARCとMRCでクラスを実装する時の違いを見てみます。

プロパティを書いてしまえばいいんですが、手動で実装したとしましょう。ARCです

f:id:niwatako:20170915180111j:plain

MRCです

f:id:niwatako:20170915180143j:plain

deallocでもreleaseをしたり。一つのインスタンスに対してこれだけコードを書きました。当時はみなさん苦労したのではないでしょうか。

そこで利用するのがObjective-C++です。

f:id:niwatako:20170915180227j:plain

Objective-CC++を共存して書けるということです。

f:id:niwatako:20170915180302j:plain

拡張子を .mm にすると書けます。C++との機能の呼び方の違いを見てみましょう。

f:id:niwatako:20170915180344j:plain

だいたいこのような似た機能があります。

どんなコードが書けるのでしょうか

f:id:niwatako:20170915180503j:plain

同じ関数の下2行はC++で書いています。

これはC++です。返り値の型にもC++の型がかけます。

f:id:niwatako:20170915180610j:plain

C++の関数と同じように書けます。

逆にC++のクラス定義にObjective-Cを書くこともできます。

f:id:niwatako:20170915180743j:plain

メモリ管理はどうするでしょう

f:id:niwatako:20170915180755j:plain

CppClass *pointer = new CppClass();
delete pointer

new で作って delete で消します。参照カウントはありません。 1回のnewに対して1回のdelete。

組み込み型と同じような書き方ではこうです。

f:id:niwatako:20170915180937j:plain

Cpp Class value_1;
...... // 書き方はいろいろありますが全部同じです
auto value_5 = CppClass(5)

値型のような扱いで、スコープを抜けると開放されます。

f:id:niwatako:20170915181016j:plain

if の中で宣言すれば、出る時に開放されます。

f:id:niwatako:20170915181048j:plain

メンバ変数はデストラクタが呼ばれると開放されます。

f:id:niwatako:20170915181123j:plain

Objective-CのクラスにC++のメンバ変数を定義するとどうなるでしょう。

[super dealloc] を呼ぶと開放されます。(allocが呼ばれた時点でコンストラクタが呼ばれています)

Objective-Cのオブジェクトを保持するC++のクラスを作るとMRCでも手軽に管理できないか

f:id:niwatako:20170915181230j:plain

サンプルコードの一つのコードです

f:id:niwatako:20170915181315j:plain

objc_ptr.hpp というクラス。チョットデキルひとなら簡単に読めると思います。

こんな構造です。

f:id:niwatako:20170915181354j:plain

shared_ptrはC++で参照カウント方式でオブジェクトを管理するものです。new や delete をいい感じにしてくれるもの。

Objective-CのものをC++でshared_ptrを使えば、開放し忘れないという考えです。

f:id:niwatako:20170915181536j:plain

TestClassをautoreleaseした状態で、objc_ptr にTestClassを保持させます

* 演算子を作って、先頭につければ中身が取り出せるようにしました。

参照カウントをそのまま保持するパターン

f:id:niwatako:20170915181658j:plain

auto で変数の型推論をつかってシンプルに。

autoreleaseを外して保持するパターンです。

f:id:niwatako:20170915181813j:plain

autoreleaseをほうっておくとメモリが溜まっていくので最初から外してしまおうというやつです。使うかどうかは必要次第ということで

これは弱参照で保持します

f:id:niwatako:20170915181911j:plain

強参照されたものを渡すと弱参照で保持します。 lock を呼ぶと強参照で取得できる。既に開放されていれば中身はありません。

weak self を使いたいときのために .to_weak も用意しました。

f:id:niwatako:20170915181959j:plain

objc_ptrをインスタンス変数で使う例です。

f:id:niwatako:20170915182036j:plain

ARCとMRCの違いをお見せした時と同じ構成で作っています。

MRC

f:id:niwatako:20170915182136j:plain

作ったやつを利用

f:id:niwatako:20170915182144j:plain

ほぼARCのように使えるようになっています。

f:id:niwatako:20170915182159j:plain

使うはobjc_ptrを取り回すのが良いと思います。

改良の余地、展望

f:id:niwatako:20170915182257j:plain

ARC環境下かMRCかでretainやreleaseをマクロで切り替えてコードを書くことがありますが、それも吸収して同じ書き方出かけるようになるのではないか

QA

  • Objective-C++とObjective-Cは相性が良いと思うが、どのような必要でこういうことが必要になったのでしょうか
    • オーディオプログラミングを良くするのですが、ARCをONにしていると音に影響がある気がしたことがあって、できるだけARCを避けたい。
      • 音質?スピード?
        • スピードですかね、途切れるような気がしました。ガベージコレクションのときも明らかに影響がありました。なのでARCも試したら、影響がある気がしました。もしかすると私の思い違いかもしれませんが。
  • ライブラリでMRCを使っていて、本体でARCをONにしている場合、本体を触るエンジニアがライブラリでARCで書いてしまう事故がよくある。そういうのの対策方法、良いアイディアはありますか
    • CocoaPodsを使って勝手にやってもらうというのはありませんか。設定が違ってもファイル単位で設定できる。ライブラリをいじることは、、、ありますか?
      • 内製だとあります
        • うーん、それは、知見はないですねぇ。。
  • Objective-C++はどれくらい新しいC++の文法を使えますか
    • C++のバージョン、は、Xcodeのbuild設定で、いま普通に作ると11、14も選べます。
      • 会場から: 「17もいけますよ、互換性を気にしなれば今はサポートが早いです」