Как те битовые маски на самом деле работают?

Например, этот метод от NSCalendar берет битовую маску:

- (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts

Таким образом, опции могут быть похожими:

NSUInteger options = kCFCalendarUnitYear;

или как:

NSUInteger options = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay;

То, что я не получаю, как это на самом деле сделано? Я имею в виду: Как они могут вытащить те значения, в которые объединяются options? Если я хотел программировать что-то вроде этого, которое может взять битовую маску, как это посмотрело бы?

26
задан dontWatchMyProfile 31 March 2010 в 17:31
поделиться

5 ответов

Для этого вы хотите поразрядно И проверяемое значение по маске, а затем посмотреть, равен ли результат операции И самой маске:

if ((options & kCFCalendarUnitYear) == kCFCalendarUnitYear) {
   // do whatever
}
32
ответ дан 28 November 2019 в 06:08
поделиться

Битовые маски работают, потому что в двоичном формате каждая степень двойки (т. Е. 2 ​​ 0 = 1, 2 1 = 2, 2 1 = 4) занимает одну позицию в последовательности битов. Например:

decimal | binary 
1       | 0001
2       | 0010
4       | 0100
8       | 1000

Когда вы или (оператор | на языках типа C) два числа a и b вместе в c , вы говорите: «возьмите биты, которые находятся в a , b или в обоих, и поместите их в c ». Поскольку степень двойки представляет собой одну позицию в двоичной строке, перекрытия нет, и вы можете определить, какие из них были установлены. Например, если мы или 2 и 4

0010 | 0100 = 0110

, обратите внимание, как они в основном объединились. С другой стороны, если мы или 5 и 3:

decimal | binary 
5       | 0101
3       | 0011

0101 | 0011 = 0111

заметим, что у нас нет способа сказать, какие биты откуда пришли, потому что двоичное представление каждого из них перекрывается.

Это становится более очевидным на еще одном примере. Возьмем числа 1, 2 и 4 (все степени двойки)

0001 | 0010 | 0100 = 0111

Это тот же результат, что и 5 | 3 ! Но поскольку исходные числа являются степенями двойки, мы можем однозначно сказать, откуда взялся каждый бит.

16
ответ дан 28 November 2019 в 06:08
поделиться

Я обнаружил, что Calculator.app полезен при визуализации битовых масок. (Просто выберите «Просмотр»> «Программист», а затем нажмите кнопку «Показать двоичный файл»). (Вы можете щелкнуть любой из 0 или 1 в двоичной таблице, чтобы включить или выключить эти биты; или введите числа в десятичном или шестнадцатеричном формате (используйте 8 | 10 | 16 NSSegmentedControl для переключения между различными представлениями)).

0
ответ дан 28 November 2019 в 06:08
поделиться

Главное - помнить, что каждое из этих значений, которые вы объединяете в "options", на самом деле просто число. Я не уверен, насколько вы знакомы с двоичной системой счисления, но вы можете представить это в десятичной системе и просто складывать числа, а не объединять их.

Допустим, A=10, B=100, и C=1000

Если бы вы хотели установить options = A+B, то options было бы равно 110. Вызванный вами метод затем посмотрит на место "десятков" для A, место "сотен" для B и место "тысяч" для C. В этом примере 1 находится на месте сотен и на месте десятков, поэтому метод будет знать, что A и B были заданы в опциях.

Это немного отличается, поскольку компьютеры используют двоичную, а не десятичную систему счисления, но я думаю, что идея очень похожа, и иногда легче думать об этом в знакомой системе счисления.

6
ответ дан 28 November 2019 в 06:08
поделиться

Битмаски на самом деле довольно просты. Вы можете думать об этом так (C#, пока кто-нибудь не сможет преобразовать):

public enum CalendarUnits
{
    kCFCalendarUnitDay = 1, // 001 in binary
    kCFCalendarUnitMonth = 2, // 010 in binary
    kCFCalendarUnitYear = 4, // 100 in binary
}

Затем вы можете использовать битовые операторы для объединения значений:

// The following code will do the following
// 001 or 100 = 101
// So the value of options should be 5
NSUInteger options = kCFCalendarUnitDay | kCFCalendarUnitYear;

Эта техника также часто используется в процедурах безопасности:

public enum Priveledges
{
    User = 1,
    SuperUser = 2,
    Admin = 4
}

// SuperUsers and Admins can Modify
// So this is set to 6 (110 binary)
public int modifySecurityLevel = SuperUser | Admin;

Затем, чтобы проверить уровень безопасности, вы можете использовать битовые операторы и посмотреть, есть ли у вас достаточное разрешение:

public int userLevel = 1;
public int adminLevel = 4;

// 001 and 110 = 000 so this user doesn't have security
if(modifySecurityLevel & userLevel == userLevel)

// but 100 and 110 = 100 so this user does
if(modifySecurityLevel & adminLevel == adminLevel)
    // Allow the action
36
ответ дан 28 November 2019 в 06:08
поделиться