Runtime系列:位运算在OC中的取值和赋值【01】

35 阅读3分钟

/** 

 *

 *   0000 0111

 * & 0000 0010

 *-------------

 *   0000 0010

 *	

 *	二进制转Bool类型需要两次取反(!!)

 */



2、赋值


2.1、当设置值为1时

掩码 + 位或(|)运算。


/**

 *   0000 0101

 * | 0000 0010

 *-------------

 *   0000 0111



 */



2.2、当设置值为0时

掩码取反(~) + 再进行位与(&)运算。


/**

 *   0000 0111

 * & 1111 1101

 *-------------

 *   0000 0101

 */



知道了位运算的基本规则,下面用几个例子,来了解一下位运算在OC中的应用:

二、位运算符赋值

===========================================================================

这里我们就用高富帅(tall、rich、handsome)的取值和赋值方法,作为举例。

2.1、代码示例


Person.h


#import <Foundation/Foundation.h>



@interface Person : NSObject



- (void)setTall:(BOOL)tall;

- (void)setRich:(BOOL)rich;

- (void)setHandsome:(BOOL)handsome;



// 高

- (BOOL)tall;

// 富

- (BOOL)rich;

// 帅

- (BOOL)handsome;



@end



Person.m


// 1、10进制位赋值

//#define kTallMask 1

//#define kRichMask 2

//#define kHandsomeMask 4



// 2、2进制位赋值,0b:表示2进制位

//#define kTallMask 0b00000001

//#define kRichMask 0b00000010

//#define kHandsomeMask 0b00000100



// 3、运算符赋值

#define kTallMask (1<<0)

#define kRichMask (1<<1)

#define kHandsomeMask (1<<2)



@interface Person(){

		// char:长度一个字节(8个二进制位)

    char _tallRichHandsome;// 0b 0000 0000

}

@end



@implementation Person



- (void)setTall:(BOOL)tall {

    if (tall) {

    		// 掩码,位或(|)运算

        _tallRichHandsome |= kTallMask;

    } else {

    		//掩码取反,进行位与(&)运算

        _tallRichHandsome &= ~kTallMask;

    }

}



- (void)setRich:(BOOL)rich {

    if (rich) {

        _tallRichHandsome |= kRichMask;

    } else {

        _tallRichHandsome &= ~kRichMask;

    }

}



- (void)setHandsome:(BOOL)handsome {

    if (handsome) {

        _tallRichHandsome |= kHandsomeMask;

    } else {

        _tallRichHandsome &= ~kHandsomeMask;

    }

}



- (BOOL)tall {

		// 掩码,位与(&)运算:两次取反(!!)返回Bool类型

    return !!(_tallRichHandsome & kTallMask);

}



- (BOOL)rich {

    return !!(_tallRichHandsome & kRichMask);

}



- (BOOL)handsome {

    return !!(_tallRichHandsome & kHandsomeMask);

}



@end





main.m


#import <Foundation/Foundation.h>

#import "Person.h"



int main(int argc, const char * argv[]) {

    @autoreleasepool {

    

        Leader *person = [[Leader alloc] init];

        person.tall = YES;

        person.rich = YES;

        person.handsome = YES;

        

        NSLog(@"-------tall:%hhd",person.tall);

        NSLog(@"-------rich:%hhd",person.rich);

        NSLog(@"-------handsome:%hhd",person.handsome);



    }

    return 0;

}



2.2、结果分析


1、mask标记:表示掩码,一般用来位运算的。

2、掩码(mask)赋值有多种方式:

1、可以用10进制位赋值的。

2、用2进制位赋值的。

3、位移运算符赋值的,这里会用到左移运算符(<<)。

其中位移运算符赋值是最简单直观的,推荐使用这种方式。

3、掩码 + 位与(&)运算转Bool类型,需要两次取反(!!)。

三、结构体+位域实现

=============================================================================

3.1、代码示例



#import <Foundation/Foundation.h>



@interface Student : NSObject



- (void)setTall:(BOOL)tall;

- (void)setRich:(BOOL)rich;

- (void)setHandsome:(BOOL)handsome;



- (BOOL)tall;

- (BOOL)rich;

- (BOOL)handsome;



@end




#import "Student.h"



@interface Student(){

    struct {

        char tall : 1;

        char rich : 1;

        char handsome : 1;

    }_tallRichHandsome;

}

@end



@implementation Student



- (instancetype)init {

    if (self = [super init]) {

        

    }

    return self;

}



- (void)setTall:(BOOL)tall {

    

    _tallRichHandsome.tall = tall;



}



- (void)setRich:(BOOL)rich {

    _tallRichHandsome.rich = rich;

}



- (void)setHandsome:(BOOL)handsome {

    _tallRichHandsome.handsome = handsome;

}



- (BOOL)tall {

    return !!_tallRichHandsome.tall;

}



- (BOOL)rich {

    return !!_tallRichHandsome.rich;

}



- (BOOL)handsome {

    return !!_tallRichHandsome.handsome;

}



3.2、分析


当取值的时候,如果用如下方式实现:


- (BOOL)handsome {

    return _tallRichHandsome.handsome;

}



发现,取值时打印出来为-1,实际二进制值为(0b1111 1111),这不是我们想要的结果,怎么处理?


/**

* tallRichHandsome.tall = 0b1                一个二进制位

*  BOOL                 = 0b0000 0000        一个字节为8个二进制位 

* ----------------------------------------------------------

*                   结果 = 0b1111 1111 = 255(-1)

* 分析:当一个二进制位赋值给一个8个二进制位时,前面不足的拿最左边值(1)的填补。

*/



那么这个问题这么处理呢?

1、取值时,因为拿到的是bool类型,可以两个取反获取到想要的值(如上面👆的例子)。

第一次取反,只要不等于0,返回的都是false,二次取反,得到我们需要的值true。

2、根据位于临近填补的特点,设置占位为两位(如:0b01),得到的就是:0b0000 0001

四、结构体+位域优化

=============================================================================

根据上面的问题处理,这里只要修改如下部分代码:


@interface Student(){

    struct {// 结构体位域长度设置为2

        char tall : 2;

        char rich : 2;

        char handsome : 2;

    }_tallRichHandsome;

}

@end



// 取值不在需要二次取反

- (BOOL)tall {

    return _tallRichHandsome.tall;

}





缺点:

结构体位域存储方式,变量在结构体中的位置,直接决定了在二进制中的存储位置。如代码有增删改操作,容易定位不准确,导致出错。

五、共用体(推荐)

============================================================================

这是OC内部底层实现的逻辑。

5.1、代码示例



#import <Foundation/Foundation.h>



@interface Leader : NSObject



- (void)setTall:(BOOL)tall;

- (void)setRich:(BOOL)rich;

- (void)setHandsome:(BOOL)handsome;



- (BOOL)tall;

- (BOOL)rich;

- (BOOL)handsome;



@end




#import "Leader.h"

#define kTallMask (1<<0)

#define kRichMask (1<<1)

#define kHandsomeMask (1<<2)



@interface Leader(){

    union {// 共用体:大家都共用一个字节

        char bits;//位

        struct {// 结构体仅是提高代码可读性,没有实际应用

            char tall : 1;

            char rich : 1;

            char handsome : 1;

        };

    }_tallRichHandsome;

}

@end



@implementation Leader



- (void)setTall:(BOOL)tall {

    if (tall) {

        _tallRichHandsome.bits |= kTallMask;

    } else {

        _tallRichHandsome.bits &= ~kTallMask;

    }

}



- (void)setRich:(BOOL)rich {

    if (rich) {

        _tallRichHandsome.bits |= kRichMask;

    } else {

        _tallRichHandsome.bits &= ~kRichMask;

    }

}



- (void)setHandsome:(BOOL)handsome {

    if (handsome) {

        _tallRichHandsome.bits |= kHandsomeMask;

    } else {

        _tallRichHandsome.bits &= ~kHandsomeMask;

    }

}



- (BOOL)tall {

    return !!(_tallRichHandsome.bits & kTallMask);

}



- (BOOL)rich {

    return !!(_tallRichHandsome.bits & kRichMask);

}



- (BOOL)handsome {

    return !!(_tallRichHandsome.bits & kHandsomeMask);

}



@end



5.2、分析


这是位运算与结构体的位域结合方式,利用各自的优势。

那有什么优势呢?

1、位运算:定位精准,提高运算效率。

2、结构体的位域:提高代码的可读性。

六、OC中的应用

===========================================================================

1、ISA_MASK