问题
You know in Cocoa there is this thing, for example you can create a UIView and do:
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
I have a custom UIView with multiple states, which I have defined in an enum like this:
enum DownloadViewStatus {
FileNotDownloaded,
FileDownloading,
FileDownloaded
};
For each created subview, I set its tag: subview1.tag = FileNotDownloaded;
Then, I have a custom setter for the view state which does the following:
for (UIView *subview in self.subviews) {
if (subview.tag == viewStatus)
subview.hidden = NO;
else
subview.hidden = YES;
}
But what I am trying to do, is to allow this:
subview1.tag = FileNotDownloaded | FileDownloaded;
So my subview1 shows up in two states of my view. Currently, it doesn't show up in any of those two states since the | operator seems to add the two enum values.
Is there a way to do that?
回答1:
Declaring Bitmasks:
Alternatively to assigning absolute values (1, 2, 4, …) you can declare bitmasks (how these are called) like this:
typedef enum : NSUInteger {
FileNotDownloaded = (1 << 0), // => 00000001
FileDownloading = (1 << 1), // => 00000010
FileDownloaded = (1 << 2) // => 00000100
} DownloadViewStatus;
or using modern ObjC's NS_OPTIONS/NS_ENUM macros:
typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
FileNotDownloaded = (1 << 0), // => 00000001
FileDownloading = (1 << 1), // => 00000010
FileDownloaded = (1 << 2) // => 00000100
};
(see Abizern's answer for more info on the latter)
The concept of bitmasks is to (usually) define each enum value with a single bit set.
Hence ORing two values does the following:
DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101
which is equivalent to:
00000001 // FileNotDownloaded
| 00000100 // FileDownloaded
----------
= 00000101 // (FileNotDownloaded | FileDownloaded)
Comparing Bitmasks:
One thing to keep in mind when checking against bitmasks:
Checking for exact equality:
Let's assume that status is initialized like this:
DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101
If you want to check if status equals FileNotDownloaded, you can use:
BOOL equals = (status == FileNotDownloaded); // => false
which is equivalent to:
00000101 // (FileNotDownloaded | FileDownloaded)
== 00000100 // FileDownloaded
-----------
= 00000000 // false
Checking for "membership":
If you want to check if status merely contains FileNotDownloaded, you need to use:
BOOL contains = (status & FileNotDownloaded) != 0; // => true
00000101 // (FileNotDownloaded | FileDownloaded)
& 00000100 // FileDownloaded
-----------
= 00000100 // FileDownloaded
!= 00000000 // 0
-----------
= 00000001 // 1 => true
See the subtle difference (and why your current "if"-expression is probably wrong)?
回答2:
While @Regexident has provided an excellent answer - I must mention the modern Objective-C way of declaring Enumerated options with NS_OPTIONS:
typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
FileNotDownloaded = 0,
FileDownloading = 1 << 0,
FileDownloaded = 1 << 1
};
Further Reference:
- NSHipster
- I Am The Walrus
回答3:
enum DownloadViewStatus {
FileNotDownloaded = 1,
FileDownloading = 2,
FileDowloaded = 4
};
This will let you perform bitwise OR's and AND's effectively.
回答4:
Useful function you can use for bitmask checking to improve readability.
BOOL bitmaskContains(NSUInteger bitmask, NSUInteger contains) {
return (bitmask & contains) != 0;
}
来源:https://stackoverflow.com/questions/16166895/declaring-and-checking-comparing-bitmask-enums-in-objective-c