2012-09-09 45 views
24

我想學習位掩碼。據我所知,這是將某種類型的二進制值存儲到一個變量中的方法。目標中的掩碼C

如果上述假設是真的,我想我可以做這樣的事情:

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是,我怎麼分配更多的性狀一個人呢?

問題2是,我必須將不斷增加的枚舉項數,或者有沒有辦法表明這一點?

最後,我想實現這樣的事情:

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

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

如果我理解正確的話,的

john.traits值應爲100011,右讀,代表特定的每個地方枚舉值/特徵.. 0意味着沒有它,1意味着它。

你可以請教關於語法的建議,並解釋一個特定的方面,如果需要?

+19

是故意的,'TraitsCharacterHonest'有一個數值爲零?你不能在這裏使用零,因爲'x&0 == 0'用於任何整數'x',即這個特性永遠不會出現在任何人身上。在這個世界上沒有誠實的人。傷心的地方...... – Dirk

+0

它就像普通的老C一樣 - Objective-C中沒有特別的設施用於位重要值。 –

+0

這不是故意的,我沒有意識到。當然,第一項必須有1的值。 正確的順序是2的冪,是嗎? 2to0,2 to1,2to2 ie..1,2,4,8,16..etc .. –

回答

59

我建議改變幾件事情:

  • 枚舉值是可以改變的是一個左移。在我看來,寫起來容易一點。

  • 你不需要typedef到NSUInteger,你可以直接使用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... 

此外,語法等除去性狀操作混亂。你將不得不使用&以及未編定,

// 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); 

您將得到合理的輸出。

+0

我真的很感激你的榜樣。 也許我不能幸運地使用Person來學習這一點,因爲在一個真實世界的項目中(由於法律原因,我不能提示問題領域),我需要通過每個特定的枚舉項來指示對特定的答案題。所以上下文不是那個人具有一組特徵的某些特徵,而是每個問題的值(YES或NO)是什麼。 (1個問題= 1與0或1的枚舉項目) 它是呈現您的提案不那麼適合然後。我不知道雖然..是嗎? –

+0

嗯。在這種情況下,我會使用NSDictionary。使用上面的字符串常量作爲字典的鍵,並使用NSNumber布爾對象來存儲答案。 – joerick

+2

我還建議在每個變量上放置一個FlagsMask後綴,表示該變量有一點(或者如果將它們合併在一起......'BoldItalicTraitsMask ='BoldTraitsMask | ItalicTraitsMask')設置並且可以用作掩碼。 – bbum

1

這裏您的主要問題是製作traits指針。刪除該指針,就像你在C將做到這一點:

john.traits |= TraitsCharacterOptimistic | TraitsCharacterOptimistic | TraitsCharacterOptimistic; 

請記住,你只需要三分一對夫婦在Objective-C的情況:

  • 當你與實際物體處理(派生自NSObject
  • 當您需要通過引用傳遞一個基元(參數int *返回一個函數返回計數),在這種情況下,您獲取本地變量的地址,並且該指針不被該函數保存。
  • 當你需要一個原始類型的數組時,在堆上動態分配(例如使用malloc &朋友)。

否則,只需使用堆棧分配的基元類型,因爲您可以用它做很多事情。

+0

該原語數組不需要動態分配。 – 2012-09-09 15:13:14

+0

@ H2CO3當然不是,這僅僅是爲什麼使用指向原始類型的指針的一個例子。 –

+0

使它成爲一個指針只是肌肉記憶錯誤。當然,這是一個原始價值。 –

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的語言中一樣。 檢查本教程Bitwise Operators in C and C++: A Tutorial

+0

我可以以某種方式避免明確地將這些2的冪賦值給枚舉值嗎? 我看過這個1 << 0..is它是我需要的語法嗎? –

+0

是的,你可以使用這個語法1 << 0,1 << 1,1 << 2等等 – tikhop

+0

我是否需要爲所有人做 - 或 - 在第一個2之後......編譯器意識到規則適用於其餘的部分? –

3

您可以遇到的一個問題是,使用位掩碼指示集內的成員資格可能會受基礎數據類型中位數的限制。例如,32位的無符號長整數只能容納32個不相交或不同的成員。如果你需要添加一個33rd,除非你使用64位無符號整數,否則你運氣不好。

對此的一種解決方法是使用字節數組。使用這種方法,您必須將位成員資格指定爲兩個數據,即字節的偏移量和用於特定位的位掩碼。

我也看到有人使用字節數組來獲得單個成員資格,所以不是使用一位,而是使用整個字節。這可能會浪費內存,但它可能會更靈活有效,浪費的內存量也不成問題。

對於使用字節數組來保存位組,您可以考慮使用無符號長整型來表示集合中的成員,其中最低有效字節是位掩碼,其餘字節用作字節數組中的無符號3字節偏移量。然後,您會做類似如下:

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++,您可以爲此功能創建自己的類,並提供各種類型的邏輯操作,以便您可以創建各種類型的集合,然後對集合執行邏輯操作。

+0

這是一個非常笨拙的解決方案,如果你使用C++,那麼'std :: bitset'(或者'std :: vector ')在這裏更有意義。 –

+1

我同意使用已存在的東西通常是最好的。另一方面,如果它不存在,那麼你必須建立一些東西。我提供這個作爲可以做什麼而不是應該做什麼的一個例子。問題來自想要學習位掩碼操作的人。 –

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/

+1

我認爲你的例子完全忽略了這一點。不錯,這是宣佈枚舉的新形式,但我在這裏詢問有關位掩碼的內容,這在您的示例中不會發生。 –

+1

對於位掩碼,您應該使用NS_OPTIONS!它甚至在你提供的鏈接中這樣說。 – orkoden

+1

@EarlGrey我實際上想說NS_OPTIONS以某種方式輸入NS_ENUM(非常尷尬..),我已經更新了我的答案。 – Zipme

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 zerotrue,因此整個條件是true。享受:-)