问题
I am trying to look for a good answer to the concept of BOOL. As I understand, a zero is a false value, and non-zero is a true value. Thinking this I assigned string length into a BOOL variable. Now, this works fine for iOS > 6. On iOS 6 I see strange results. For string with odd length, isEnabled is turing True but for strings with even length, isEnabled is turing False.
BOOL isEnabled = myString.length;
Please help me understand this. Of course, now I've been assigning expressions to BOOL variable like this:
BOOL isEnabled = myString.length > 0;
Exact code:
- (void)textViewDidChange:(UITextView *)iTextView {
self.navigationItem.rightBarButtonItem.enabled = [[self.noteTextView.text trim] length];
}
PS: As mentioned, myString is changing in length; from 'a' to 'ab' to 'abc' etc.
回答1:
However, I could not understand the iOS 6 behaviour for enabling the button for odd ones only. [...] I was curious to know the root cause.
To explain the observed behavior there are two technical details to explain: The type of Objective-C's BOOL and implementation details of UIKit.
Type of BOOL
The differing behavior is actually not related to the iOS version but to the device's architecture. There's a difference on how the iOS SDK defines a BOOL for 32 versus 64 bit architectures. See this excerpt from objc.h:
#if !defined(OBJC_HIDE_64) && TARGET_OS_IPHONE && __LP64__
typedef bool BOOL;
#else
typedef signed char BOOL;
#endif
So BOOL on 32 bit can have any value between 0 and 255, while on 64 bit it is compiler enforced to only have the values 0 or 1. You can easily try this by running the following line on the simulator, set to iPhone 4s (32 bit) or iPhone 6 (64 bit).
NSLog(@"%d", (BOOL)2);
This is the explanation for why you see differing behavior on different devices. But where does the even-odd thing come from?
Implementation details of UIBarButtonItem
There's another subtle technical detail involved. You are actually setting the enabled property of a UIBarButtonItem.
Apple likes to use a space saving scheme to store flags in their UI components. A BOOL (on both 32 and 64 bit architectures) would always use at least one byte, but only one bit of information is needed. So they are using bit fields to store the actual value.
Excerpt from iOS SDK 8.4 UIBarButtonItem.h (shortened for clarity):
@interface UIBarButtonItem : UIBarItem {
struct {
unsigned int enabled:1;
} _barButtonItemFlags;
}
The magic is the :1 behind the enabled field in the _barButtonItemFlags struct. This defines the bit-field of width one. To connect the enabled property with this bit-field, they have to implement custom accessors. Here's an example for the setter:
- (void)setEnabled:(BOOL)enabled {
_barButtonItemFlags.enabled = enabled;
}
So what happens when we're doing this:
someBarButtonItem.enabled = 2;
The compiler understands that it has to call the setEnabled: method with an argument of 2.
On 64 bit architectures the 2 would be converted to _Bool, where the standard says this has to result in a value of 1. On 32 bit systems this is not the case, leaving the original value of 2 in place, before calling the method.
Inside of setEnabled: the value is assigned to an unsigned int of width 1. The C standard says that when assigning an unsigned integer of greater width the remaining bits are just dropped. The result is that setEnabled: just stores the lowest bit of the argument in _barButtonItemFlags.enabled. The lowest bit is one on odd numbers, zero on even numbers.
Conclusion
All of the above behavior is within the semantics provided by the standard. There is no undefined behavior involved. It's just an unfortunate fact that the expected behavior of BOOL differs from what you actually get on 32 bit architectures.
回答2:
In earlier times BOOL was implemented as an integer type. Therefore it was possible with such an implementation to assign a value > 1 to it and reading it back. But this had been wrong (because relying on an implementation detail) from the very beginning, because the documentation always said that the valid values for an (C) object of type BOOL are NO and YES. Period.
Starting in 2014(? 2013?) the BOOL type became more like the C _Bool type. That means that assigning a value > 1 to an object of that type, 1 (YES) is stored into the variable. So, in any case the behavior changed. (An undefined behavior became a defined behavior and the behavior is different to the older undefined one.)
But your problem looks like that only Bit0 is stored. (It is 0 for even numbers and 1 for odd number.) This would be a bug. Please show us the complete code.
However, you can use _Bool instead of BOOL.
回答3:
Try using this ,
BOOL isEnabled;
if ( ( myString.length % 2 ) == 0 ) {
//Is even
isEnabled=FALSE;
}
else {
//Is odd
isEnabled=TRUE;
}
来源:https://stackoverflow.com/questions/31982311/assigning-nsuinteger-to-bool-conceptual-understanding