Например, этот метод от NSCalendar берет битовую маску:
- (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts
Таким образом, опции могут быть похожими:
NSUInteger options = kCFCalendarUnitYear;
или как:
NSUInteger options = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay;
То, что я не получаю, как это на самом деле сделано? Я имею в виду: Как они могут вытащить те значения, в которые объединяются options
? Если я хотел программировать что-то вроде этого, которое может взять битовую маску, как это посмотрело бы?
Для этого вы хотите поразрядно И проверяемое значение по маске, а затем посмотреть, равен ли результат операции И самой маске:
if ((options & kCFCalendarUnitYear) == kCFCalendarUnitYear) {
// do whatever
}
Битовые маски работают, потому что в двоичном формате каждая степень двойки (т. Е. 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
! Но поскольку исходные числа являются степенями двойки, мы можем однозначно сказать, откуда взялся каждый бит.
Я обнаружил, что Calculator.app полезен при визуализации битовых масок. (Просто выберите «Просмотр»> «Программист», а затем нажмите кнопку «Показать двоичный файл»). (Вы можете щелкнуть любой из 0 или 1 в двоичной таблице, чтобы включить или выключить эти биты; или введите числа в десятичном или шестнадцатеричном формате (используйте 8 | 10 | 16 NSSegmentedControl для переключения между различными представлениями)).
Главное - помнить, что каждое из этих значений, которые вы объединяете в "options", на самом деле просто число. Я не уверен, насколько вы знакомы с двоичной системой счисления, но вы можете представить это в десятичной системе и просто складывать числа, а не объединять их.
Допустим, A=10, B=100, и C=1000
Если бы вы хотели установить options = A+B, то options было бы равно 110. Вызванный вами метод затем посмотрит на место "десятков" для A, место "сотен" для B и место "тысяч" для C. В этом примере 1 находится на месте сотен и на месте десятков, поэтому метод будет знать, что A и B были заданы в опциях.
Это немного отличается, поскольку компьютеры используют двоичную, а не десятичную систему счисления, но я думаю, что идея очень похожа, и иногда легче думать об этом в знакомой системе счисления.
Битмаски на самом деле довольно просты. Вы можете думать об этом так (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