Catégorie pour la size d'un UIViewController

colas_colas_ Membre
décembre 2014 modifié dans API UIKit #1

Bonsoir !


 


Je reviens sur une question déjà  posée : déterminer la taille d'une vue qui "vit" dans un xib. Comme j'en avais marre de toujours écrire le même code, j'ai fait une petite catégorie.


 


Je voulais avoir vos avis sur comment l'améliorer. En particulier, est-ce possible de protéger les accès concurrents ? Est-ce possible de n'exécuter le code qu'une seule fois par sous-classe de UIViewController (faire un dispatch_once dont le token serait possédé par la sous-classe, en quelque sorte) ?


 


Sinon, pensez-vous que ce code est dangereux ?


 


Merci !



#import "UIViewController+CBDSize.h"


static NSMutableDictionary * _dictionnaryForSizes ;


@implementation UIViewController (CBDSize)

+ (CGSize)size_cbd_
{
NSString * nameClass = NSStringFromClass([self class]) ;

if (!_dictionnaryForSizes[nameClass])
{
if (!_dictionnaryForSizes)
{
_dictionnaryForSizes = [NSMutableDictionary new] ;
}

/*
Loading the nib
*/
NSArray * objects = [[NSBundle mainBundle] loadNibNamed:nameClass
owner:[[self alloc] init]
options:0] ;

UIView * firstView = [objects firstObject] ;


_dictionnaryForSizes[nameClass] = [NSValue valueWithCGSize:firstView.frame.size] ;
}


NSValue * value = _dictionnaryForSizes[nameClass] ;

return [value CGSizeValue] ;
}

@end
 

Réponses

  • AliGatorAliGator Membre, Modérateur
    décembre 2014 modifié #2
    1) Utilise dispatch_once, pour te protéger des accès concurrents
    2) Scope ta variable statique uniquement à  l'intérieur de ta méthode, pour limiter les accès externes
    3) Préfère NSCache à  NSMutableDictionary pour ce genre d'usage
  • colas_colas_ Membre
    décembre 2014 modifié #3

    Merci Ali pour ton aide ! 


     


    OK pour le 2).


     


    Pour le 3), je ne comprends pas pourquoi tu me dis que NSCache est mieux que NSMutableDictionary. En effet, n'y a-t-il pas un risque que le cache soit vidé et que, à  cause du dispatch_once la size ne soit pas recréée ?

     


    Pour le 1), mon problème c'est qu'il faut que cette méthode ne soit appelée qu'une seule fois par classe. Exemple : MyVC et MyVCbis héritent tous les deux de UIViewController. Il faut que la size soit calculée une fois si j'appelle [MyVC size_cbd_] et une autre fois si j'appelle [MyVCbis size_cbd_] ; 


     


    Je ne sais pas trop comment faire.


     


     


    Par exemple, après test, le code qui suit ne marche pas.  [MyVC size_cbd_] donne la bonne valeur mais [MyVCbis size_cbd_] donne {0,0}



    + (CGSize)size_cbd_
    {
    static NSMutableDictionary * _dictionnaryForSizes ;
    NSString * nameClass = NSStringFromClass([self class]) ;


    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    if (!_dictionnaryForSizes)
    {
    _dictionnaryForSizes = [NSMutableDictionary new] ;
    }

    /*
    Loading the nib
    */
    NSArray * objects = [[NSBundle mainBundle] loadNibNamed:nameClass
    owner:[[self alloc] init]
    options:0] ;

    UIView * firstView = [objects firstObject] ;


    _dictionnaryForSizes[nameClass] = [NSValue valueWithCGSize:firstView.frame.size] ;
    });




    NSValue * value = _dictionnaryForSizes[nameClass] ;

    return [value CGSizeValue] ;
    }

    Merci !


  • AliGatorAliGator Membre, Modérateur
    Quand je te parlais de dispatch_once, ce n'était pas forcément pour TOUT ton code, c'est juste pour allouer le_dictionaryForSizes (enfin, du coup, le _cacheForSizes) qu'une seule fois, c'était pour remplacer ton "if (!x) { x = [... new]; }" quoi.
    Bref, pour initialiser l'instance de ta variable statique (que ce soit un NSMutableDictionary ou un NSCache). Pas pour la remplir.

    Et du coup ton instance de NSCache ne sera créée qu'une fois mais ça n'empêche pas que ton NSCache, lui, sera interrogé à  chaque fois, pour chaque classe et/ou sous-classe.
  • AliGatorAliGator Membre, Modérateur

    @implementation UIViewController (CBDSize)

    + (CGSize)viewSize_cbd_
    {
    static NSCache * sizesCache;
    dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ sizesCache = [NSCache new]; });

    NSString * className = NSStringFromClass([self class]);
    NSValue * value = sizesCache[nameClass] ;
    if (!value)
    {
    NSArray * objects = [[NSBundle mainBundle] loadNibNamed:className
    owner:nil
    options:0] ;
    UIView * firstView = [objects firstObject] ;

    value = [NSValue valueWithCGSize:firstView.frame.size];
    sizesCache[nameClass] = value;
    }
    return [value CGSizeValue] ;
    }
    @end
  • OK, c'est plus logique, oui.


     


    Pourquoi me conseilles-tu NSCache plutôt que NSDictionary ?


     


    Merci !


  • AliGatorAliGator Membre, Modérateur
    Parce que cette classe est dédiée à  ce genre d'usage, et qu'elle sait libérer de l'espace toute seule quand il y en a besoin.

    Certes pour ton cas particulier, tu ne stockes que des @(CGSize) donc c'est pas ça qui va risquer de déclencher un memory warning, mais bon, sur le principe c'est une bonne pratique d'utiliser les classes qui sont dédiées à  un usage particulier quand tu as besoin de cet usage ;)
Connectez-vous ou Inscrivez-vous pour répondre.