Crash sur variable statique

NiClouNiClou Membre
novembre 2014 modifié dans API UIKit #1

J'obtiens un crash (aléatoire) sur un retour du mode Background sur le morceau de code ci-dessous:



+ (NSString *) NLSystemUserAgent {
static NSString *_userAgent;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
UIWebView *webView = [[[UIWebView alloc] initWithFrame:CGRectZero] autorelease];
_userAgent = [webView stringByEvaluatingJavaScriptFromString:@navigator.userAgent];
});
return _userAgent;
}

J'ai un bad_access sur _userAgent.

Une idée? 


Pour info je n'utilise pas ARC.


 


Voici la solution que je propose (pas glop):



+ (NSString *)NLSystemUserAgent {
static NSString *_userAgent;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
_userAgent = [[webView stringByEvaluatingJavaScriptFromString:@navigator.userAgent] retain];
[webView release];
});

if (_userAgent)
[[NLUserDefault standardUserDefaults] setObject:_userAgent forKey:@NL_UserAgent];
else {
NSString* userAgent = [[NLUserDefault standardUserDefaults] objectForKey:@NL_UserAgent];
if (userAgent)
_userAgent = userAgent;
else _userAgent = @Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257 ;
//Pas Glop, mais ça ne devrait pas arriver...
}

return _userAgent;
}

Merci d'avance.


Réponses

  • AliGatorAliGator Membre, Modérateur
    1) Ne pas utiliser ARC + ne pas respecter les conventions de nommage (méthode qui a tout d'un nom de classe et pas d'un nom de méthode) + appel d'une méthode UI sans s'assurer qu'elle est dispatchée sur le mainThread (surtout pour un singleton qui peut être appelé de n'importe où) = amas de mauvaises idées. Ca ne m'étonne pas trop que ça crash (surtout le risque de non-UI thread)

    Après je sais pas si c'est la cause de ton EXC_BADACCESS, mais bon ça pourrait être une bonne idée de protéger tout cela comme il faut quand même.

    2) En plus, utiliser une UIWebView (donc composant d'UI) juste pour récupérer le User-Agent (= info métier) je suis pas sûr que ce soit la meilleure des solutions (même si c'est celle qu'on trouve le + sur Google, ça veut pas dire que c'est la meilleure, on trouve bien quelques autres aberrations sur Google concernant certaines pratiques iOS...), je serais toi je chercherai un moyen plus propre s'il en existe


  • 1) Ne pas utiliser ARC + ne pas respecter les conventions de nommage (méthode qui a tout d'un nom de classe et pas d'un nom de méthode) + appel d'une méthode UI sans s'assurer qu'elle est dispatchée sur le mainThread (surtout pour un singleton qui peut être appelé de n'importe où) = amas de mauvaises idées. Ca ne m'étonne pas trop que ça crash (surtout le risque de non-UI thread)


    Après je sais pas si c'est la cause de ton EXC_BADACCESS, mais bon ça pourrait être une bonne idée de protéger tout cela comme il faut quand même.


    2) En plus, utiliser une UIWebView (donc composant d'UI) juste pour récupérer le User-Agent (= info métier) je suis pas sûr que ce soit la meilleure des solutions (même si c'est celle qu'on trouve le + sur Google, ça veut pas dire que c'est la meilleure, on trouve bien quelques autres aberrations sur Google concernant certaines pratiques iOS...), je serais toi je chercherai un moyen plus propre s'il en existe





    + (NSString *) systemUserAgent {
    static NSString *_userAgent;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
    _userAgent = [[webView stringByEvaluatingJavaScriptFromString:@navigator.userAgent] retain];
    [webView release];
    });
    });

    if (_userAgent)
    [[NLUserDefault standardUserDefaults] setObject:_userAgent forKey:@NL_UserAgent];
    else {
    NSString* userAgent = [[NLUserDefault standardUserDefaults] objectForKey:@NL_UserAgent];
    if (userAgent)
    _userAgent = userAgent;
    else _userAgent = @Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257 ;
    //Pas Glop, mais ça ne devrait pas arriver...
    }

    return _userAgent;
    }

    Tu penses que ça pourrait résoudre le problème?

     

  • AliGatorAliGator Membre, Modérateur
    A part que :
    - la logique avec les UserDefaults est bizarre (puisque tu ne vas chercher l'info dedans QUE si ton userAgent n'a pas (encore) été initialisé, au lieu qu'ils overrident la valeur comme c'est sensé être le cas d'usage classique
    - pourquoi un dispatch_async, rendant ainsi ta méthode asynchrone alors qu'elle continue de retourner la valeur de manière synchrone ?
  • Joanna CarterJoanna Carter Membre, Modérateur

    Si je peux te demander, pourquoi éviter l'ARC ?


  • NiClouNiClou Membre
    novembre 2014 modifié #6


    Si je peux te demander, pourquoi éviter l'ARC ?




     


    C'est historique dans ce projet ^^


     


     




    A part que :

    - la logique avec les UserDefaults est bizarre (puisque tu ne vas chercher l'info dedans QUE si ton userAgent n'a pas (encore) été initialisé, au lieu qu'ils overrident la valeur comme c'est sensé être le cas d'usage classique

    - pourquoi un dispatch_async, rendant ainsi ta méthode asynchrone alors qu'elle continue de retourner la valeur de manière synchrone ?




     


    T'as raison pour le userDefault. J'me suis embrouillé entre temps.


    Le dispatch_async, c'est pour chopper la mainQueue. Lors du premier appel la valeur peut etre différente, mais ensuite ça corrige. En soit ça n'est pas très grave.


  • AliGatorAliGator Membre, Modérateur

    Le dispatch_async, c'est pour chopper la mainQueue.

    Heu ? C'est quoi le rapport ?! Quel rapport entre "passer en async" et "chopper la mainQueue" ?
    Pourquoi ne pas rester en synchrone ? Faut juste faire attention si tu es déjà  sur le mainThread, à  ne pas faire un dispatch_sync sur lui-même, car dans ce cas tu aurais un DeadLock. Mais sinon, c'est inutile et pas logique de passer en asynchrone... surtout si tu continues de retourner une valeur avec un "return" à  la fin !


    dispatch_block_t block = ^{ /* ton code */ };
    if ([NSThread isMainThread]) {
    block();
    } else {
    dispatch_sync(dispatch_get_main_queue(), block);
    }
  • Merci Ali.

    Je ne savais pas qu'on pouvait faire comme ça.


Connectez-vous ou Inscrivez-vous pour répondre.