web-dev-qa-db-fra.com

Déclaration et vérification / comparaison d'énumérations (masque de bits) dans Objective-C

Vous savez dans Cocoa il y a cette chose, par exemple vous pouvez créer un UIView et faire:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

J'ai un UIView personnalisé avec plusieurs états, que j'ai défini dans un enum comme ceci:

enum DownloadViewStatus {
  FileNotDownloaded,
  FileDownloading,
  FileDownloaded
};

Pour chaque sous-vue créée, j'ai défini son tag: subview1.tag = FileNotDownloaded;

Ensuite, j'ai un setter personnalisé pour l'état d'affichage qui fait ce qui suit:

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

Mais ce que j'essaie de faire, c'est de permettre ceci:

subview1.tag = FileNotDownloaded | FileDownloaded;

Alors mon subview1 apparaît dans deux états de mon point de vue. Actuellement, il n'apparaît dans aucun de ces deux états depuis le | L'opérateur semble ajouter les deux valeurs d'énumération.

Y-a-t-il un moyen de faire ça?

76
thibaultcha

Déclaration de Bitmasks:

Vous pouvez également attribuer des valeurs absolues (1, 2, 4,…), Vous pouvez déclarer bitmasks (comment ils sont appelés) comme ceci:

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

ou en utilisant les macros NS_OPTIONS/NS_ENUM d'ObjC modernes:

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

(voir réponse d'Abizern pour plus d'informations sur ce dernier)

Le concept des masques de bits est de définir (généralement) chaque valeur d'énumération avec un seul ensemble de bits.

Par conséquent, ORing deux valeurs effectue les opérations suivantes:

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

ce qui équivaut à:

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

Comparaison des bitmasks:

Une chose à garder à l'esprit lors de la vérification des masques de bit:

Vérification de l'égalité exacte:

Supposons que le statut soit initialisé comme ceci:

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

Si vous voulez vérifier si status est égal à FileNotDownloaded, vous pouvez utiliser:

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

ce qui équivaut à:

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

Vérification de "l'adhésion":

Si vous voulez vérifier si status contient simplement FileNotDownloaded, vous devez utiliser:

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

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

Voir la subtile différence (et pourquoi votre expression "si" actuelle est probablement fausse)?

272
Regexident

Alors que @Regexident a fourni une excellente réponse - je dois mentionner la manière moderne Objective-C de déclarer les options énumérées avec NS_OPTIONS:

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

Référence supplémentaire:

19
Abizern

Fonction utile que vous pouvez utiliser pour la vérification du masque de bits pour améliorer la lisibilité.

BOOL bitmaskContains(NSUInteger bitmask, NSUInteger contains) {
    return (bitmask & contains) != 0;
}
1
Renetik
enum DownloadViewStatus {
  FileNotDownloaded = 1,
  FileDownloading = 2,
  FileDowloaded = 4
};

Cela vous permettra d'effectuer efficacement les OR et ET au niveau du bit.

1
mah