NSData 生成時に渡したデータの扱われ方 : Objective-C プログラミング

PROGRAM


NSData 生成時に渡したデータの扱われ方

Objective-C では NSData を使って任意のデータを扱えるようになっています。

このインスタンスを生成するために、持たせるデータを指定しますが、その引数として渡したデータの扱われ方について細かく整理してみたいと思います。

新たにデータ用メモリ確保する方法

まず、NSData を引数に取って別の NSData インスタンスを生成する場合です。

NSData* data = [[NSData alloc] initWithData:sourceData];

このように initWithData: メソッドを使ってインスタンスを生成した場合、引数に渡した NSData (sourceData) が保持している値は、別のメモリ領域が確保されてそこに格納されます。

つまり data.bytes と sourceData.bytes とは、異なるアドレスを指すことになります。

 

続いて、C 言語の配列を引数に取って NSData インスタンスを生成する場合です。

たとえば NSInteger 型のデータが 100 個入った配列があったとして、それを引数に取って NSData インスタンスを生成する initWithBytes:length: メソッドについて見てみます。

NSData* data = [[NSData alloc] initWithBytes:integerArray length:sizeof(NSInteger) * 100];

このようにすると、引数に渡した NSInteger の配列 (integerArray) とは別のメモリが確保されて、そこへ配列の値がコピーされた NSData が取得されます。

つまり data.bytes と integerArray とは、異なるアドレスを指すことになります。

 

このような NSData の生成の場合、データを保持するメモリの管理が NSData インスタンス自身に委ねられるので、そのインスタンスが解放されるまでの間、確実にそのデータを参照することができます。

メモリがコピーされる分、データのサイズが大きいほど負荷が大きくなりますけど、取得後は自由に使えて便利です。

 

元のデータ用メモリを使いまわす方法

重い処理を避けたかったり、ローカルスコープで一時的に変換して使うような場面であれば、メモリをコピーしない方が負荷が減って効率的です。

NSData インスタンスを生成するときに、渡したデータをコピーせずに NSData で引き続き使いたい場合には initWithBytesNoCopy:length:freeWhenDone: メソッドを使用します。

NSData* data = [[NSData alloc] initWithBytesNoCopy:integerArray length:sizeof(NSInteger) * 100 freeWhenDone:NO];

このようにすると、引数に渡した NSInteger の配列 (integerArray) のメモリ領域がそのまま NSData のデータとして使用されます。

つまり data.bytes と integerArray とが、同じアドレスを指すことになります。

 

ところで、ここでは引数 freeWhenDone に NO を指定しています。

これは、NSData が引き続き使用することになったメモリ領域(ここでは integerArray)を、この NSData インスタンスが解放されたときに解放しないようにする、ということを意味しています。

既存のデータを一時的に使うようなときには、このように freeWhenDone を NO に指定しておかないと、NSData を使い終わったタイミングで元のメモリが解放されてしまいます。

 

逆に、データを加工するための領域を C 配列で生成して加工してから、それを使って最後に NSData インスタンスを生成して返すような場合には、この freeWhenDone を YES にます。

NSData* data = [[NSData alloc] initWithBytesNoCopy:integerArray length:sizeof(NSInteger) * 100 freeWhenDone:YES];

こうすることで、一時バッファーの内容をコピーしてから削除するという無駄なメモリ操作がなくなりますし、その NSData インスタンスが解放されるときには、再利用したメモリが自動的に解放されるので、効率がとても良くなります。

解放のときには free 関数が使われるので、渡すメモリは malloc 系の関数で確保されている必要があります。

 

ちなみに、この freeWhenDone を取らない initWithBytesNoCopy:length: メソッドも用意されています。

NSData* data = [[NSData alloc] initWithBytesNoCopy:integerArray length:sizeof(NSInteger) * 100];

この場合は freeWhenDone を YES にしたのと同じように、生成された NSData は指定されたメモリを使いまわしつつ、インスタンス解放時にはそのメモリを一緒に解放するようになります。

 

copy メソッドを使った場合

ところで、NSData は NSCopying プロトコルに準拠しているので、copy メソッドを使うことができます。

NSData* data = [sourceData copy];

ただし、このようにして取得した NSData のインスタンスは、上記のどの方法を使って作成したインスタンスであっても、そのインスタンス自身と同じものになります。

今回の例であれば sourceData とまったく同じ、つまり "data == sourceData" が成り立ちます。

 

copy メソッドはあくまでも NSData インスタンスの複製であって、メモリ領域をコピーしたい場合には initWithData: メソッドや initWithBytes:length: メソッドを使用する必要があります。

[ もどる ]