25

ビットマスキングについて学びたいです。私が理解している限りでは、特定の型のバイナリ値を 1 つの変数に格納する手段です。

上記の仮定が正しい場合、次のようなことができると考えました。

typedef NSUInteger Traits;

enum
{
    TraitsCharacterHonest       = 0,
    TraitsCharacterOptimistic   = 1,
    TraitsCharacterPolite       = 4,
    TraitsCharacterDevious      = 8,
    TraitsPhysicalTall          = 16,
    TraitsPhysicalBeautiful     = 32,
    TraitsPhysicalFat           = 64,
    TraitsPhysicalBigEyes       = 128,
    TraitsPhysicalRedHair       = 256, 
};

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (strong, nonatomic) NSString  *name;
@property (assign, nonatomic) Traits    *traits;

@end

質問 1は、1 人の人物により多くの特性を割り当てるにはどうすればよいですか?

質問 2は、列挙型アイテムにますます多くの数値を入力する必要がありますか、またはこれを示す方法はありますか?

最終的には、次のようなことを達成したいと考えています。

Person *john = [[Person alloc] init];

//here code that assigns john three traits: TraitsCharacterHonest,      
//TraitsCharacterOptimistic and TraitsPhysicalBeautiful.

正しく理解すれば、

john.traits は 100011. である必要があります。右から読み取り、各場所はその特定の列挙値/特性を表し、0 はそれがないことを意味し、1 はそれがあることを意味します。

必要に応じて、構文に関するアドバイスと特定の側面について説明していただけますか?

4

6 に答える 6

60

いくつか変更することをお勧めします。

  • 列挙値は、左シフトされたものに変更できます。私の意見では、少し書きやすくなります。

  • NSUInteger に typedef する必要はありません。 を使用して列挙型を直接宣言できますtypedef enum

  • また、他の人が言及したように、プロパティは Traits タイプへのポインターであってはなりません。

私のコードは次のようになります。

typedef enum
{
    TraitsCharacterHonest       = 1 << 0,
    TraitsCharacterOptimistic   = 1 << 1,
    TraitsCharacterPolite       = 1 << 2,
    TraitsCharacterDevious      = 1 << 3,
    TraitsPhysicalTall          = 1 << 4,
    TraitsPhysicalBeautiful     = 1 << 5,
    TraitsPhysicalFat           = 1 << 6,
    TraitsPhysicalBigEyes       = 1 << 7,
    TraitsPhysicalRedHair       = 1 << 8
} Traits;

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (strong, nonatomic) NSString  *name;
@property (assign, nonatomic) Traits     traits;

@end

John の特性を設定すると、次のようになります。

Person *john = [[Person alloc] init];

john.traits = TraitsCharacterHonest | TraitsCharacterOptimistic | TraitsPhysicalBeautiful;

ただし、ビットフィールドは学習するのに役立ちますが、デバッグするのは本当に面倒です。このキャラクターの特性をすぐに印刷したい場合は、次のようなコードを記述する必要があります。

NSMutableString *result = [NSMutableString string];

if (self.traits & TraitsCharacterHonest)
{
    [result appendString: @"Honest, "];
}
if (self.traits & TraitsCharacterOptimistic)
{
    [result appendString: @"Optimistic, "];
}
if (self.traits & TraitsCharacterPolite)
{
    [result appendString: @"Polite, "];
}
// etc...

さらに、特性の削除などの操作の構文は混乱を招きます。&NOT ed 定数を使用する必要があります。

// remove 'Tall' trait
john.traits = john.traits & ~TraitsPhysicalTall

可能であれば (そしてパフォーマンスはそれほど問題ではありません)、より高いレベルの機能を使用することをお勧めします。おそらく、文字列定数を持つ NSSet でしょうか? 例えば

__unused static NSString *TraitsCharacterHonest = @"TraitsCharacterHonest";
__unused static NSString *TraitsCharacterOptimistic = @"TraitsCharacterOptimistic";
__unused static NSString *TraitsCharacterPolite = @"TraitsCharacterPolite";
// etc...

@interface Person : NSObject

@property (strong, nonatomic) NSString     *name;
@property (assign, nonatomic) NSMutableSet *traits;

@end

次に、次のことができます。

// adding
[john.traits addObject: TraitsCharacterHonest];
// checking
[john.traits containsObject: TraitsCharacterHonest];
// removing 
[john.traits removeObject: TraitsCharacterHonest];

私にはもっと理にかなっています。さらに、特性の説明を直接印刷できます

NSLog(@"John's traits: %@", john.traits);

妥当な出力が得られます。

于 2012-09-09T15:43:56.500 に答える
4

発生する可能性のある問題の 1 つは、ビット マスクを使用してセット内のメンバーシップを示すことが、基になるデータ型のビット数によって制限される可能性があることです。たとえば、32 ビットの unsigned long には、32 個のばらばらのメンバーまたは異なるメンバーのためのスペースしかありません。33 番目を追加する必要がある場合は、64 ビットの符号なし整数にしない限り、うまくいきません。

これに対する 1 つの回避策は、バイト配列を使用することです。このアプローチでは、ビット メンバーシップを 2 つのデータとして指定する必要があります。バイトへのオフセットと、特定のビットに使用するビット マスクです。

また、1 つのメンバーシップにバイト配列を使用して、1 ビットを使用するのではなく、バイト全体を使用する人も見てきました。これはメモリの浪費になる可能性がありますが、より柔軟で有用であり、無駄なメモリの量が問題にならない可能性があります。

ビットのセットを保持するためにバイトの配列を使用する場合、最下位バイトがビット マスクであり、残りのバイトが符号なし 3 バイトとして使用されるセットのメンバーを表すために unsigned long を使用することを検討してください。バイト配列へのオフセット。次に、次のようなことを行います。

int getBitSet (unsigned char *bArray, unsigned long ulItem)
{
    unsigned long ulByteOffset = ((ulItem >> 8) & 0x00ffffff);
    unsigned char ucByteMask = (ulItem & 0x000000ff);

    return (*(bArray + ulByteOffset) & ucByteMask);
}

int setBitSet (unsigned char *bArray, unsigned long ulItem, unsigned long ulNewValue)
{
    unsigned char oldValue;
    unsigned long ulByteOffset = ((ulItem >> 8) & 0x00ffffff);
    unsigned char ucByteMask = (ulItem & 0x000000ff);

    oldValue = *(bArray + ulByteOffset) & ucByteMask;

    if (ulNewValue) {
        *(bArray + ulByteOffset) |= ucByteMask;  // set bit
    } else {
        *(bArray + ulByteOffset) &= ~ucByteMask;  // clear bit
    }

    return oldValue;
}

次に、一連の関数を使用してバイトを取得および設定するか、マクロを使用できます。C++ を使用すると、この機能用に独自のクラスを作成し、さまざまな種類の論理演算を提供して、さまざまな種類のセットを作成し、そのセットに対して論理演算を実行できます。

于 2012-09-09T14:59:25.683 に答える
2

ここでのあなたの主な問題はtraits、ポインタを作ることです。ポインタをドロップし、Cの場合と同じように実行します。

john.traits |= TraitsCharacterOptimistic | TraitsCharacterOptimistic | TraitsCharacterOptimistic;

Objective-Cのいくつかの状況でのみポインタが必要になることに注意してください。

  • 実際のオブジェクト(から派生NSObject)を処理している場合
  • プリミティブを参照(int *カウントを返す関数への引数)で渡す必要がある場合。この場合、ローカル変数のアドレスを取得し、そのポインターは関数によって保存されません。
  • プリミティブ型の配列が必要な場合は、ヒープに動的に割り当てられます(例:malloc&friendsを使用)。

それ以外の場合は、スタックに割り当てられたプリミティブ型を使用します。これを使用すると、さまざまなことができます。

于 2012-09-09T14:24:17.053 に答える
2

iOS 6 以降、Mac OS X 10.8 以降

できるよ:

typedef NS_OPTIONS(NSUInteger, Traits) {
    TraitsCharacterHonest,
    TraitsCharacterOptimistic,
    TraitsCharacterPolite,
    TraitsCharacterDevious,
    TraitsPhysicalTall,
    TraitsPhysicalBeautiful,
    TraitsPhysicalFat,
    TraitsPhysicalBigEyes,
    TraitsPhysicalRedHair
};

詳細については、http://nshipster.com/ns_enum-ns_options/を参照してください。

于 2014-01-21T14:52:42.580 に答える
1

まず、次のように変更します。

...
typedef NSUInteger Traits;

enum
{
    TraitsCharacterHonest = 0, //cann't be a 0
    ...
};
...
@property (assign, nonatomic) Traits *traits; //you no need create a pointer to a primitive type

に: ...

typedef NSUInteger Traits;

enum
{
    TraitsCharacterHonest = 1, 
    ...
};
...
@property (assign, nonatomic) Traits traits;

割り当てるには、次のことを行う必要があります。

john.traits |= TraitsCharacterHonest | TraitsCharacterDevious;

ObjC でのビット単位の操作は、言語と同じCです。このチュートリアルを確認してくださいC および C++ のビット単位演算子: チュートリアル

于 2012-09-09T14:31:48.190 に答える
1

仮定:

1 << 8は何と同じ100000000です:

john.traits = TraitsCharacterHonest | TraitsCharacterOptimistic | TraitsPhysicalBeautiful;

は次とまったく同じです:

john.traits = 000000001 | 000000010 | 000100000;

結果は次のとおりです。

john.traits = 000100011

今、条件をチェックしたいとき:

if (self.traits & TraitsCharacterHonest) { ... }

それは以下と同等です:

if (000100011 & 000000001) { ... } 

その結果は次のとおりです。

if (000000001) { ... }

そして、これは実際には であり1not zero値はtrueであるため、条件全体はtrueです。楽しみ:-)

于 2015-03-13T18:59:05.317 に答える