I've got 5 states in my app, and I use BOOL flags to mark them. But it isn't straightforward, because I have to write 5 lines to change all flags when I want to change state.
Can you write some ideas or simple code to solve this problem?
code:
//need to choose second state
flag1 = false;
flag2 = true;
flag3 = false;
flag4 = false;
flag5 = false;
Also, it's to bad because I can choose 2 states one time.
P.S.
I found modern and more Apple-way. Answer below.
Use typedef enum to define all possible states using bitmasks.
Note this will give you a maximum of up to 64 different states (on most platforms). If you need more possible states, this solution will not work.
Handling this scheme will require you to fully understand and safely handle boolean algebra.
//define all possible states
typedef enum
{
stateOne = 1 << 0, // = 1
stateTwo = 1 << 1, // = 2
stateThree = 1 << 2, // = 4
stateFour = 1 << 3, // = 8
stateFive = 1 << 4 // = 16
} FiveStateMask;
//declare a state
FiveStateMask state;
//select single state
state = stateOne; // = 1
//select a mixture of two states
state = stateTwo | stateFive; // 16 | 2 = 18
//add a state
state |= stateOne; // 18 | 1 = 19
//remove stateTwo from our state (if set)
if ((state & stateTwo) == stateTwo)
{
state ^= stateTwo; // 19 ^ 2 = 17
}
//check for a single state (while others might also be selected)
if ((state & stateOne) == stateOne)
{
//stateOne is selected, do something
}
//check for a combination of states (while others might also be selected)
if ((state & (stateOne | stateTwo)) == stateOne | stateTwo)
{
//stateOne and stateTwo are selected, do something
}
//the previous check is a lot nicer to read when using a mask (again)
FiveStateMask checkMask = stateOne | stateTwo;
if ((state & checkMask) == checkMask)
{
//stateOne and stateTwo are selected, do something
}
You can always use a byte (unsigned char) size variable using its' bits
as flags (each bit acts as one BOOL flag).
Good instructions to set/clear/toggle/check a bit is here.
Offcourse you'd want to set kind of human readable names for this
flags, i.e.:
#define flag1 1
#define flag2 2
#define flag3 4
#define flag4 8
#define flag5 16
Nowadays we have got another option for flags. It is NS_ENUM.
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
First arg for type and second for name.
Related
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?
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)?
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
enum DownloadViewStatus {
FileNotDownloaded = 1,
FileDownloading = 2,
FileDowloaded = 4
};
This will let you perform bitwise OR's and AND's effectively.
Useful function you can use for bitmask checking to improve readability.
BOOL bitmaskContains(NSUInteger bitmask, NSUInteger contains) {
return (bitmask & contains) != 0;
}
I am trying to implement the following typedef
typedef NS_OPTIONS (NSInteger, MyCellCorners) {
MyCellCornerTopLeft,
MyCellCornerTopRight,
MyCellCornerBottomLeft,
MyCellCornerBottomRight,
};
and correctly assign a value with
MyCellCorners cellCorners = (MyCellCornerTopLeft | MyCellCornerTopRight);
when drawing my cell, how can I check which of the options match so I can correctly draw it.
Use bit masking:
typedef NS_OPTIONS (NSInteger, MyCellCorners) {
MyCellCornerTopLeft = 1 << 0,
MyCellCornerTopRight = 1 << 1,
MyCellCornerBottomLeft = 1 << 2,
MyCellCornerBottomRight = 1 << 3,
};
MyCellCorners cellCorners = MyCellCornerTopLeft | MyCellCornerTopRight;
if (cellCorners & MyCellCornerTopLeft) {
// top left corner set
}
if (etc...) {
}
The correct way to check for this value is to first bitwise AND the values and then check for equality to the required value.
MyCellCorners cellCorners = MyCellCornerTopLeft | MyCellCornerTopRight;
if ((cellCorners & MyCellCornerTopLeft) == MyCellCornerTopLeft) {
// top left corner set
}
The following reference explains why this is correct and provides other insights into enumerated types.
Reference: checking-for-a-value-in-a-bit-mask
I agree with NSWill. I recently had a similar issue with wrong comparison.
The right if statement should be:
if ((cellCorners & MyCellCornerTopLeft) == MyCellCornerTopLeft){
I need to write a method like this:
-(void)doStuff:(int)options;
Based in the a typedef enum like this:
typedef enum
{
FirstOption,
SecondOption,
ThirdOption
} MyOptions
What I need to to do in order to be able to call the method in this way (i.e. calling the method with more than one option "enabled":
[self doStuff:(FirstOption | ThirdOption)];
Do I need to setup the typedef enum differently? And how I check the options received in the method, a simple if (options == ...)?
What you call for is a bit array. Define your options like this:
enum MyOptions
{
FirstOption = 0x01, // first bit
SecondOption = 0x02, // second bit
ThirdOption = 0x04, // third bit
... = 0x08 ...
};
Combine the options like you suggested, with |, and test for them with & (options & SecondOption).
- (void) doStuff: (MyOptions) options
{
if ( options & FirstOption )
{
// do something fancy
}
if ( options & SecondOption )
{
// do something awesome
}
if ( (options & SecondOption) && ( options & ThirdOption) )
{
// do something sublime
}
}
I'm no expert at Obj-C, but in other languages I would use flags. If each value is a power of two, then it will have only one bit set for that value. You can use that bit as a bool for a particular option.
FirstOption = 1
SecondOption = 2
ThirdOption = 4
FirstAndThird = FirstOption | ThirdOption; // Binary Or sets the bits
// Binary And checks the bits
IsFirst = FirstAndThird & FirstOption == FirstOption; // is true
IsSecond = FirstAndThird & SecondOption == SecondOption; // is false
IsThird = FirstAndThird & ThirdOption == ThirdOption; // is true
This question may also be of use.
I am using bitflag to enable/disable some functionality in my code. I have following enum.
typedef enum function {
function1 = 1 << 0,
function2 = 1 << 1,
function3 = 1 << 2,
function4 = 1 << 3
};
I know that I need to use following code to check which flag has been enabled.
if((flags & function1) == function1)
{
// do some action
}
In my enum, number of flags is large and to check every flag, I need to have that many "if" condition checks. Is there any way which can minimize the number of if statements required? I am new to objective C and looking to implement it in objective C. Thanks in advance.
Yes, combine the flags via a bitwise or:
if (flags & (function1 | function2 | function3 | function4))
{
// any of the flags has been set
}
Moreover, to check for a particular flag you don't need the part == function1, flags & function1 is sufficient as it will evaluate either to zero or function1.
I have an enum declaration using bit flags and I cant exactly figure out on how to use this.
enum
{
kWhite = 0,
kBlue = 1 << 0,
kRed = 1 << 1,
kYellow = 1 << 2,
kBrown = 1 << 3,
};
typedef char ColorType;
I suppose to store multiple colors in one colorType I should OR the bits together?
ColorType pinkColor = kWhite | kRed;
But suppose I would want to check if pinkColor contains kRed, how would I do this?
Anyone care to give me an example using the provided ColorType example ?
Yes, use bitwise OR (|) to set multiple flags:
ColorType pinkColor = kWhite | kRed;
Then use bitwise AND (&) to test if a flag is set:
if ( pinkColor & kRed )
{
// do something
}
The result of & has any bit set only if the same bit is set in both operands. Since the only bit in kRed is bit 1, the result will be 0 if the other operand doesn't have this bit set too.
If you need to get whether a particular flag is set as a BOOL rather than just testing it in an if condition immediately, compare the result of the bitwise AND to the tested bit:
BOOL hasRed = ((pinkColor & kRed) == kRed);