C構造体をNSArray
に保存する通常の方法は何ですか?長所、短所、メモリ処理?
特に、valueWithBytes
とvalueWithPointer
の違いは何ですか?-以下のジャスティンとナマズによって発生します。
リンクはこちら 将来の読者のためにvalueWithBytes:objCType:
についてのAppleの議論へ...
一部のラテラル思考とパフォーマンスの詳細については、EvgenはSTL::vector
inC++を使用する問題を提起しました。
(それは興味深い問題を提起します:STL::vector
とは異なり、高速なcライブラリがありますが、それは最小限の「配列の整然とした処理」を可能にします...?)
元の質問...
例えば:
typedef struct _Megapoint {
float w,x,y,z;
} Megapoint;
それで、そのような独自の構造をNSArray
に格納するための通常の、最良の、慣用的な方法は何ですか、そしてそのイディオムでどのようにメモリを処理しますか?
構造体を格納する通常のイディオムを特に探していることに注意してください。もちろん、新しい小さなクラスを作成することで問題を回避できます。ただし、実際に構造体を配列に入れる通常のイディオムを知りたいのですが、ありがとうございます。
ところで、ここにNSDataアプローチがありますか?良くない...
Megapoint p;
NSArray *a = [NSArray arrayWithObjects:
[NSData dataWithBytes:&p length:sizeof(Megapoint)],
[NSData dataWithBytes:&p length:sizeof(Megapoint)],
[NSData dataWithBytes:&p length:sizeof(Megapoint)],
nil];
BTWを参照点として、Jarret Hardieに感謝します。CGPoints
をNSArray
に保存する方法は次のとおりです。
NSArray *points = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
[NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
nil];
( 簡単な方法でCGPointオブジェクトをNSArrayに追加するにはどうすればよいですか? を参照してください)
NSValue はCoreGraphics構造をサポートするだけではありません。独自の構造にも使用できます。クラスはおそらく単純なデータ構造のNSData
よりも軽いので、そうすることをお勧めします。
次のような式を使用します。
[NSValue valueWithBytes:&p objCType:@encode(Megapoint)];
そして、値を元に戻すには:
Megapoint p;
[value getValue:&p];
NSValue
ルートに固執することをお勧めしますが、NSArray(およびCocoaの他のコレクションオブジェクト)にプレーンな 'ol struct
データ型を保存したい場合)、そうすることができます-間接的ではありますが、Core Foundationと フリーダイヤルブリッジング を使用します。
CFArrayRef
(およびそれに対応する可変のCFMutableArrayRef
)を使用すると、開発者は配列オブジェクトをより柔軟に作成できます。指定された初期化子の4番目の引数を参照してください。
CFArrayRef CFArrayCreate (
CFAllocatorRef allocator,
const void **values,
CFIndex numValues,
const CFArrayCallBacks *callBacks
);
これにより、CFArrayRef
オブジェクトがCore Foundationのメモリ管理ルーチンを使用するように要求できます。まったく使用しないか、または独自のメモリ管理ルーチンさえ使用できます。
必須の例:
// One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types.
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
NSMutableArray *array = (NSMutableArray *)arrayRef;
struct {int member;} myStruct = {.member = 42};
// Casting to "id" to avoid compiler warning
[array addObject:(id)&myStruct];
// Hurray!
struct {int member;} *mySameStruct = [array objectAtIndex:0];
上記の例では、メモリ管理に関する問題を完全に無視しています。構造体myStruct
はスタック上に作成されるため、関数が終了すると破棄されます。配列には、もはや存在しないオブジェクトへのポインタが含まれます。独自のメモリ管理ルーチンを使用してこれを回避することができます-そのため、オプションが提供されます-しかし、参照カウント、メモリの割り当て、割り当て解除などのハードワークを行う必要があります。
このソリューションはお勧めしませんが、他の人が興味がある場合に備えてここに保管します。 :-)
(スタックの代わりに)ヒープに割り当てられた構造を使用する方法を以下に示します。
typedef struct {
float w, x, y, z;
} Megapoint;
// One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types.
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
NSMutableArray *array = (NSMutableArray *)arrayRef;
Megapoint *myPoint = malloc(sizeof(Megapoint);
myPoint->w = 42.0f;
// set ivars as desired..
// Casting to "id" to avoid compiler warning
[array addObject:(id)myPoint];
// Hurray!
Megapoint *mySamePoint = [array objectAtIndex:0];
C構造体を追加する同様の方法は、ポインターを保存し、そのようにポインターを逆参照することです。
typedef struct BSTNode
{
int data;
struct BSTNode *leftNode;
struct BSTNode *rightNode;
}BSTNode;
BSTNode *rootNode;
//declaring a NSMutableArray
@property(nonatomic)NSMutableArray *queues;
//storing the pointer in the array
[self.queues addObject:[NSValue value:&rootNode withObjCType:@encode(BSTNode*)]];
//getting the value
BSTNode *frontNode =[[self.queues objectAtIndex:0] pointerValue];
あなたがオタクを感じている場合、または実際に作成するクラスがたくさんある場合:objcクラスを動的に構築すると便利な場合があります(ref:class_addIvar
)。このようにして、任意の型から任意のobjcクラスを作成できます。フィールドごとに指定するか、構造体の情報を渡すことができます(ただし、実際にはNSDataを複製します)。時には有用ですが、ほとんどの読者にとってはおそらく「楽しい事実」です。
ここでこれをどのように適用しますか?
class_addIvarを呼び出してMegapointインスタンス変数を新しいクラスに追加するか、実行時にMegapointクラスのobjcバリアントを合成できます(Megapointの各フィールドのインスタンス変数など)。
前者はコンパイルされたobjcクラスと同等です。
@interface MONMegapoint { Megapoint megapoint; } @end
後者はコンパイルされたobjcクラスと同等です。
@interface MONMegapoint { float w,x,y,z; } @end
ivarを追加した後、メソッドを追加/合成できます。
受信側で保存されている値を読み取るには、合成メソッドobject_getInstanceVariable
、またはvalueForKey:
(これらのスカラーインスタンス変数をNSNumberまたはNSValue表現に変換することがよくあります)。
btw:受け取った回答はすべて有用です。コンテキスト/シナリオに応じて、より良い/悪い/無効な回答もあります。特定のケースに最適なのは、メモリ、速度、保守の容易さ、転送またはアーカイブの容易さなどに関する特定のニーズです...しかし、あらゆる点で理想的な「完璧な」ソリューションはありません。 「NSArrayにc-structを配置する最良の方法」はなく、「特定のシナリオ、ケース、または要件のセット」のためにNSArrayにc-structを配置する最良の方法はありません。 -指定する必要があります。
さらに、NSArrayは一般的にポインターサイズ(またはそれより小さい)型の再利用可能な配列インターフェースですが、多くの理由でc-structにより適したコンテナーが他にもあります(std :: vectorはc-structの典型的な選択です)。
複数のabis /アーキテクチャ間でこのデータを共有している場合は、貧乏人のobjcシリアライザーを使用するのが最善です。
Megapoint mpt = /* ... */;
NSMutableDictionary * d = [NSMutableDictionary new];
assert(d);
/* optional, for your runtime/deserialization sanity-checks */
[d setValue:@"Megapoint" forKey:@"Type-Identifier"];
[d setValue:[NSNumber numberWithFloat:mpt.w] forKey:@"w"];
[d setValue:[NSNumber numberWithFloat:mpt.x] forKey:@"x"];
[d setValue:[NSNumber numberWithFloat:mpt.y] forKey:@"y"];
[d setValue:[NSNumber numberWithFloat:mpt.z] forKey:@"z"];
NSArray *a = [NSArray arrayWithObject:d];
[d release], d = 0;
/* ... */
...特に、構造が時間とともに変化する可能性がある場合(またはターゲットプラットフォームによって)。他のオプションほど高速ではありませんが、一部の条件(重要であるかどうかを指定していない)で破損する可能性は低くなります。
シリアル化された表現がプロセスを終了しない場合、任意の構造体のサイズ/順序/アライメントは変更されるべきではなく、よりシンプルで高速なオプションがあります。
どちらの場合も、参照カウントされたオブジェクト(NSData、NSValueと比較して)を既に追加しているため、多くの場合、Megapointを保持するobjcクラスを作成するのが正しい答えです。
構造体に attribute _objc_boxable
_を追加し、@()
構文を使用して、_valueWithBytes:objCType:
_を呼び出さずに構造体をNSValueインスタンスに追加できます。
_typedef struct __attribute__((objc_boxable)) _Megapoint {
float w,x,y,z;
} Megapoint;
NSMutableArray<NSValue*>* points = [[NSMutableArray alloc] initWithCapacity:10];
for (int i = 0; i < 10; i+= 1) {
Megapoint mp1 = {i + 1.0, i + 2.0, i + 3.0, i + 4.0};
[points addObject:@(mp1)];//@(mp1) creates NSValue*
}
Megapoint unarchivedPoint;
[[points lastObject] getValue:&unarchivedPoint];
//or
// [[points lastObject] getValue:&unarchivedPoint size:sizeof(Megapoint)];
_
NSValueを使用すると、Obj-Cオブジェクトとして構造体を保存できますが、NSArchiver/NSKeyedArchiverで構造体を含むNSValueをエンコードすることはできません。代わりに、個々の構造体メンバーをエンコードする必要があります...
NSArrayにc構造体を配置する代わりに、NSDataまたはNSMutableDataにc構造体の配列として配置できます。それらにアクセスするには
const struct MyStruct * theStruct = (const struct MyStruct*)[myData bytes];
int value = theStruct[2].integerNumber;
または設定する
struct MyStruct * theStruct = (struct MyStruct*)[myData mutableBytes];
theStruct[2].integerNumber = 10;
C/C++型にはstd :: vectorまたはstd :: listを使用することをお勧めします。これは、最初はNSArrayよりも高速であり、2番目に十分な速度が得られない場合は、いつでも作成できるためです。 STLコンテナー用のアロケーターを使用して、それらをさらに高速にします。最新のモバイルゲーム、物理学、およびオーディオエンジンはすべて、STLコンテナを使用して内部データを保存します。彼らが本当に速いからといって。
それがあなたのためではない場合-NSValueについての人からの良い答えがあります-私はそれが最も受け入れられると思います。