Объявление и проверка/сравнение (битмаска-) перечислений в Objective-C

Вы знаете, что в Cocoa есть эта вещь, например, вы можете создать UIView и сделать:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

У меня есть пользовательский UIView с несколькими состояниями, которые я определил в enum следующим образом:

enum DownloadViewStatus {
  FileNotDownloaded,
  FileDownloading,
  FileDownloaded
};

Для каждого созданного subview я устанавливаю его tag: subview1.tag = FileNotDownloaded;

Затем у меня есть настраиваемый параметр для состояния представления, который выполняет следующие действия:

for (UIView *subview in self.subviews) {
  if (subview.tag == viewStatus)
    subview.hidden = NO;
  else
    subview.hidden = YES;
}

Но , что я пытаюсь сделать, это разрешить это:

subview1.tag = FileNotDownloaded | FileDownloaded;

Итак, мой subview1 появляется в двух состояниях моего представления. В настоящее время он не отображается ни в одном из этих двух состояний, так как оператор |, кажется, добавляет два значения перечисления.

Есть ли способ сделать это?

Ответ 1

Объявление битовых масок:

Альтернативно для назначения абсолютных значений (1, 2, 4,...) вы можете объявить битмаски (как они называются) следующим образом:

typedef enum : NSUInteger {
  FileNotDownloaded = (1 << 0), // => 00000001
  FileDownloading   = (1 << 1), // => 00000010
  FileDownloaded     = (1 << 2)  // => 00000100
} DownloadViewStatus;

или с использованием современных макросов ObjC NS_OPTIONS/NS_ENUM:

typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
  FileNotDownloaded = (1 << 0), // => 00000001
  FileDownloading   = (1 << 1), // => 00000010
  FileDownloaded    = (1 << 2)  // => 00000100
};

(см. Ответ Abizern для получения дополнительной информации о последних)

Понятие битмаски состоит в том, чтобы (обычно) определять каждое значение перечисления с помощью одного набора бит.

Следовательно, OR ввод двух значений делает следующее:

DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101

что эквивалентно:

  00000001 // FileNotDownloaded
| 00000100 // FileDownloaded
----------
= 00000101 // (FileNotDownloaded | FileDownloaded)

Сравнение битмаск:

Одна вещь, которую нужно иметь в виду при проверке битмакса:

Проверка точного равенства:

Предположим, что статус инициализирован следующим образом:

DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101

Если вы хотите проверить, если status равно FileNotDownloaded, вы можете использовать:

BOOL equals = (status == FileNotDownloaded); // => false

что эквивалентно:

   00000101 // (FileNotDownloaded | FileDownloaded)
== 00000100 // FileDownloaded
-----------
=  00000000 // false

Проверка на "членство":

Если вы хотите проверить, что status просто содержит FileNotDownloaded, вам нужно использовать:

BOOL contains = (status & FileNotDownloaded) != 0; // => true

   00000101 // (FileNotDownloaded | FileDownloaded)
&  00000100 // FileDownloaded
-----------
=  00000100 // FileDownloaded
!= 00000000 // 0
-----------
=  00000001 // 1 => true

См. тонкую разницу (и почему ваше текущее "если" -выражение, вероятно, неверно)?

Ответ 2

В то время как @Regexident предоставил отличный ответ - я должен упомянуть современный способ Objective-C объявления перечисляемых опций с помощью NS_OPTIONS:

typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
  FileNotDownloaded = 0,
  FileDownloading   = 1 << 0,
  FileDownloaded    = 1 << 1
};

Дополнительная ссылка:

Ответ 3

enum DownloadViewStatus {
  FileNotDownloaded = 1,
  FileDownloading = 2,
  FileDowloaded = 4
};

Это позволит эффективно выполнять побитовые операции OR и AND.