Le code suivant dans ViewController.mm, serait compilé avec succès (sans aucun avertissement ni erreur) dans un environnement 64 bits.

- (void)viewDidLoad {
    [super viewDidLoad];
    BOOL var = [[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];
    NSLog(@"%d", var);
}

Mais lorsque je change la cible en cours d'exécution en un appareil 32 bits (par exemple, iPhone 5), cela me montre une erreur comme celle-ci:

 Cannot initialize a variable of type 'BOOL' (aka 'signed char') with an rvalue of type 'id _Nullable'

Je sais que cette affectation est erronée, mais pourquoi est-elle autorisée dans la première situation?

1
Jintao Ou 20 nov. 2018 à 05:11

4 réponses

Meilleure réponse

Pour iOS 64 bits, le type BOOL utilise le type C99 _Bool (parfois également disponible en tant que bool). Ce type est défini pour n'avoir que deux valeurs, 0 ou 1. L'attribution d'une autre valeur à une variable de ce type la fait prendre la valeur 1. (Autrement dit, toutes les valeurs non nulles deviennent 1.)

Ainsi, l'affectation ne tronquera pas la valeur au niveau du bit, convertissant potentiellement une valeur non nulle en zéro. Par conséquent, il est «sûr» dans un certain respect et il n'y a aucune raison pour un avertissement. (Cela peut encore être surprenant pour un développeur naïf qui n'a pas compris la différence entre le pointeur d'un objet et sa valeur, comme d'autres réponses l'ont abordé, mais c'est une chose différente.)

3
Ken Thomases 20 nov. 2018 à 21:38

L'erreur indique que vous affectez un objet à une variable de type BOOL. Pour le résoudre, vous devez convertir l'objet renvoyé par objectForKey: à une variable BOOL en utilisant boolValue.

BOOL var = [[[NSUserDefaults standardUserDefaults] objectForKey:@"foo"] boolValue];
1
trungduc 20 nov. 2018 à 04:06

Plusieurs facteurs expliquent pourquoi cela fonctionne, beaucoup d'entre eux sont des choses historiques qui ne sont là que parce que c'était comme ça.

  1. bool est un type relativement nouveau. Si vous regardez du code C plus ancien, vous verrez souvent qu'ils utilisent simplement int, ou aiment le compilateur Objective-C et déclarent leur propre Bool, BOOL ou {{X4 }} type.

  2. À l'époque où Objective-C a été défini à l'origine, ils utilisaient le type le plus petit possible (signed char) pour leur type BOOL.

  3. L'instruction C if a toujours permis d'utiliser un pointeur comme condition, pour tester qu'un pointeur n'est pas NULL (ou nil dans le cas d'ObjC).

  4. Lorsque bool a été ajouté à la norme C, il était essentiellement défini comme "le type que if prend" et devait donc avoir tous les mêmes comportements, y compris vous permettre de fournir un pointeur où un booléen était attendu, et faites-le évaluer à false si nil, true sinon.

  5. Apple n'a pas pu remplacer BOOL par bool sur d'anciennes machines, car cela aurait changé le symbole que @encode() aurait renvoyé pour un BOOL et empêché les anciens programmes d'être liées dans le même processus que les nouvelles bibliothèques (et vice versa).

  6. Lorsque iOS 64 bits est arrivé, de nombreux types de données avaient déjà changé de taille, donc Apple était sûr de changer BOOL en bool là-bas, car il n'y avait pas d'anciens programmes qui devaient pouvoir s'exécuter dans le même espace mémoire comme les nouvelles bibliothèques. Tout était nouveau.

Ainsi, sur les plates-formes qui ont la nouvelle définition de BOOL comme bool, vous rencontrez la fonctionnalité de rétrocompatibilité de C qui vous permet de vérifier les pointeurs NULL. Sur les plates-formes plus anciennes, BOOL est défini comme un signed char qui ne convient évidemment pas au pointeur, et n'a pas la sauce spéciale pour les pointeurs, donc entraîne un avertissement.

1
uliwitness 31 mars 2019 à 17:11

Votre code:
BOOL var = [[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];
assigne l'adresse d'un objet à essentiellement un int d'une certaine taille ...

Ce n'est presque toujours pas ce que vous voulez. Vous voulez probablement:
BOOL var = [[[NSUserDefaults standardUserDefaults] objectForKey:@"foo"]boolValue];

Si vous souhaitez attribuer en fonction de la présence de cet objet, vous pouvez faire:
BOOL var = !![[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];

La raison en est que si les 8 derniers bits de l'adresse devaient tous arriver à 0, cela pourrait être un objet valide, mais serait une valeur fausse, qui serait le résultat de l'affectation tronquée.

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

    BOOL b = 2;
    if (b == YES)
    {
        NSLog(@"I guess you are right");
    }else{
        NSLog(@"nope");
    }

Imprime: nope

1
Grady Player 20 nov. 2018 à 21:02