源地址:转自answer-huang的博客
正如你所知的那样,电脑的资料都是以二进制存储的,当然我们编程语言中的变量也都是用二进制存储,在Cocoa和iOS编程中大量使用了位运算。通常我们接触的有这几个: <<(左移),>>(右移),&(与),|(或), ^(异或),~(非)。通过这些符号,我们可以对变量进行位元运算。
<<和>>
左移和右移的功能是移动变量中所有位元,位元向左/向右移动之后,最高位/最低为的位元会被移除并补0:
5 << 1 = 10 //5的二进制为00101,全部位元向左移动一位数后便会变成 01010
5 >> 1 = 2 //5的二进制为00101,全部位元向右移动一位数后便会变成 00010
•位元:即我们常说的Bit,指二进制中的一位,也称二进制位,是二进制最小信息单位。
十进制中,所有位数向左移动一位变为原来的十倍,向右移动一位变为原来的十分之一,二进制中也是如此,向左移动一位会变为原来的两倍,移动两位则变为四倍。对于电脑来说,进行位元运算要比乘除法快不少(在现代架构中, 情况并非如此:位运算的运算速度通常与加法运算相同,仍然快于乘法运算),所以对于性能要求较高的应用中,可以多考虑使用位元运算取代乘除法,但缺点是代码可读性就没那么高了。
&
学过C语言的应该都接触过&符号,它能将两个变量对应的位元进行’与’逻辑运算并产生新的变量。最基本的如:
0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1
两个相应的二进位都为1, 该位的结果值才为1,否则为0,假设我们现在计算85&109,根据运算规则:
109 -> 1101101
&85 -> 1010101
69 = 1000101
弄懂这个运算后我们来看一个Cocoa中的一个例子:
UIColor类中,我们不能直接通过颜色的Hex值得到一个UIColor的对象,而PS,Web中都大量的使用Hex值,所以我们需要给UIColor扩展一个Category,这样便可快速根据Hex值生成UIColor对象:
- +(UIColor *)colorWithRGBHex:(UInt32)hex alpha:(CGFloat)alpha
- {
- int r = (hex >> 16) & 0xFF;
- int g = (hex >> 8) & 0xFF;
- int b = (hex) & 0xFF;
- return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:alpha];
- }
相信很多人都用过这样的方法,但估计很少人去深究这样做的道理,下面我就详细解释一下这其中的原理:
1)这个类方法接受两个参数,一个是UInt32类型的hex值(即16进制的颜色值,eg:0xf5c018),CGFloat类型的alpha(即浮点类型的透明度)。
2)根据所给的hex值计算得到r,g,b的值。
3)调用UIColor的类方法colorWithRed:green:blue:alpha:返回一个color对象
这个方法的主要都集中在第二步,我们可以看到计算r值时先将hex右移16位,再和0xFF进行&运算。
•我们先了解一下hex值,比如0xf5c018代表了红色为f5,绿色为c0,蓝色为18的颜色(均为16进制),每个颜色组件占一个字节即8位(在iOS和OS X上最常见的格式就是大家所熟知的32bits-per-pixel(每个像素占32位),8bits-per-componet(每个颜色组件占8位)),所以这三个颜色总共占了24位(还有8位用来存放透明度)。
我们可以将0xffc018转换为二进制:11110101 11000000 00011000,此时我们再看右移16位的操作,你便会发现这个操作执行完后变为:00000000 00000000 11110101,其实是移除了绿色和蓝色的位,最终只剩下红色位上的值,接下来我们用得到的值和0xFF进行&运算:
00000000 00000000 11111111
& 00000000 00000000 11110101
->00000000 00000000 11110101
最终我们把00000000 00000000 11110101赋值给整型r,到此我们便得到红色的值:r=245。
同理将hex值右移8位便得到:00000000 11110101 11000000,然后再和0xFF进行&运算:
00000000 00000000 11111111
& 00000000 11110101 11000000
->00000000 00000000 11000000
最终我们把00000000 00000000 11000000赋值给整型g,到此我们便得到绿色的值:g=192。
同样的方法得到蓝色:b=24,得到r,g,b后我们便很容易得到UIColor对象了,其实RGB和hex之间转换的原理就是利用了进制的相互转换。
另外我们可以用&很容易的判断一个数的奇偶性,因为奇数的二进制最后一位必定为1,所以再和1进行&运算后仍为1,而偶数的二进制最后一位必定为0,所以与1进行&运算为0:
(n & 1)?奇数:偶数
|
|的功能是将两个变量对应的位元进行’或’逻辑运算并产生新的变量。最基本的如:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
两个相应的二进位中只要有一个为1, 该位的结果值为1,假设我们现在计算85|109,根据运算规则:
109 -> 1101101
| 85 -> 1010101
125 = 1111101
同样的,我们看Cocoa中的一个例子(Lancy的
这篇文章已经讲的比较详细,就直接引用了):
- typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
- UIViewAnimationOptionLayoutSubviews = 1 << 0,
- UIViewAnimationOptionAllowUserInteraction = 1 << 1,
- UIViewAnimationOptionBeginFromCurrentState = 1 << 2,
- UIViewAnimationOptionRepeat = 1 << 3,
- UIViewAnimationOptionAutoreverse = 1 << 4,
- UIViewAnimationOptionOverrideInheritedDuration = 1 << 5,
- UIViewAnimationOptionOverrideInheritedCurve = 1 << 6,
- UIViewAnimationOptionAllowAnimatedContent = 1 << 7,
- UIViewAnimationOptionShowHideTransitionViews = 1 << 8,
- UIViewAnimationOptionOverrideInheritedOptions = 1 << 9,
-
- UIViewAnimationOptionCurveEaseInOut = 0 << 16,
- UIViewAnimationOptionCurveEaseIn = 1 << 16,
- UIViewAnimationOptionCurveEaseOut = 2 << 16,
- UIViewAnimationOptionCurveLinear = 3 << 16,
-
- UIViewAnimationOptionTransitionNone = 0 << 20,
- UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20,
- UIViewAnimationOptionTransitionFlipFromRight = 2 << 20,
- UIViewAnimationOptionTransitionCurlUp = 3 << 20,
- UIViewAnimationOptionTransitionCurlDown = 4 << 20,
- UIViewAnimationOptionTransitionCrossDissolve = 5 << 20,
- UIViewAnimationOptionTransitionFlipFromTop = 6 << 20,
- UIViewAnimationOptionTransitionFlipFromBottom = 7 << 20,
- } NS_ENUM_AVAILABLE_IOS(4_0);
我们观察Apple在UIViewAnimationOptions的枚举变量,使用了一个NSUInteger就表示了UIViewAnimation所需的所有Option。其中0~9十个是互不影响的可同时存在option。16~19,20~24使用了4位来表示互斥的option。
如此定义了之后,对UIViewAnimationOptions的赋值变得尤为简单,使用 | 操作符既可以获得一个给对应的option位赋值后的结果。例如:
- [UIView animateWithDuration:1.0
- delay:0
- options:UIViewAnimationOptionAllowUserInteraction
- | UIViewAnimationOptionBeginFromCurrentState
- | UIViewAnimationOptionCurveEaseIn
- animations:{...}
- completion:{...}];
提取也比较简单,使用 & 操作符 和 >> 操作符,就可以轻松判定某个位有没有被设置,以及提取某些状态位,例如:
- UIViewAnimationOptions option = UIViewAnimationOptionAllowUserInteraction
- | UIViewAnimationOptionBeginFromCurrentState
- | UIViewAnimationOptionCurveEaseIn
- | UIViewAnimationOptionTransitionCrossDissolve;
-
- if (option & UIViewAnimationOptionAllowUserInteraction) {
- NSLog(@"UIViewAnimationOptionAllowUserInteraction has been set");
- }
- if (option & UIViewAnimationOptionBeginFromCurrentState) {
- NSLog(@"UIViewAnimationOptionBeginFromCurrentState has been set");
- }
- UInt8 optionCurve = option >> 16 & 0xf;
- if (optionCurve == 1) {
- NSLog(@"UIViewAnimationOptionCurveEaseIn has been set");
- }
- UInt8 optionTransition = option >> 20 & 0xf;
- if (optionTransition == 5) {
- NSLog(@"UIViewAnimationOptionTransitionCrossDissolve has been set");
- }
这里最需要注意的地方就是,对互斥的状态的设置必须尤为小心,如果你这么写:
- UIViewAnimationOptions badOption = UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionCurveEaseOut;
- UInt8 oops = badOption >> 16 & 0xf;
- NSLog(@"Sorry, it's not UIViewAnimationOptionCurveEaseInOut");
- NSLog(@"oops = %d, you got UIViewAnimationOptionCurveLinear", oops);
^
^的功能是将两个变量对应的位元进行’异或’逻辑运算并产生新的变量。最基本的如:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
简言之就是两个比较的位不同时才为1,相同则为0。Cocoa中^有特定的用意(block中),所以别的场合中很少用到,这里我想到一个比较经典但不常见的例子:
写程序时有时要交换两个变量的值,通常的做法是定义一个中间变量,从而将两个变量的值进行交换,而我们其实可以使用^运算符直接将两个变量进行交换:
a = a ^ b;
b = a ^ b;
a = a ^ b;
乍眼看去可能看不出什么,我们不妨假设sum=a^b。
第二步中的a替换成sum就得到b=sum^b=a^b^b=a。
第三步中的a和b同时替换掉前两步中的值就得到a=a^b^a=b。
最终顺利交换两个变量的值。
•对于a^b^b,我们可以先计算b^b,因为所有位元都相同,所以b^b=0,然后再计算a^0,因为1^0=1,0^0=0,所以a^0=a。
~
~ 0 = 1
~ 1 = 0
~的作用是将变量的每一个位元都颠倒过来,比如:
~00000000 00000000 00000000 00000101 -> 5
=11111111 11111111 11111111 11111010 ->-6