2013-09-03 4 views
8

Dans mon application iPhone, j'utilise une sous-classe de AFHTTPClient pour accéder à un service Web de repos. Je veux que toutes mes demandes soient traitées par une instance de mon client API, donc j'utilise un pattern singleton.Modèle singleton avec le paramètre

Cela fonctionne correctement lorsque le service s'exécute sur une seule URL. Je peux utiliser une valeur constante pour définir l'URL. Maintenant, dans la version finale de l'application, chaque application communiquera avec un autre service qui sera installé dans le réseau de l'entreprise. Je vais donc obtenir l'URL du service à partir d'une configuration distante. Le motif singleton est-il toujours un bon choix ici? Comment suis-je supposé le paramétrer si l'URL peut même changer pendant l'exécution de l'application?

acclamations

#import "FooAPIClient.h" 
#import "AFJSONRequestOperation.h" 

static NSString * const kFooAPIBaseURLString = @"http://192.168.0.1"; 

@implementation FooAPIClient 

+ (instancetype)sharedClient { 
    static FooAPIClient *_sharedClient = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return _sharedClient; 
} 

- (id)initWithBaseURL:(NSURL *)url { 
    self = [super initWithBaseURL:url]; 
    if (!self) { 
     return nil; 
    } 

    [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; 
    [self setDefaultHeader:@"Accept" value:@"application/json"]; 

    return self; 
} 

@end 
+0

Je pense que vous devrez écrire du code. Si l'URL change en série, il s'agit simplement d'une fonction de "changement d'URL" sur votre singleton. Si vous pouvez avoir plusieurs URL actives en même temps, vous avez besoin d'une sorte de répertoire ou d'un tableau d'instances, ou bien d'avoir l'instance "possédée" par le code qui l'utilise, vs étant adressée via un singleton. –

+0

Il ne peut y avoir qu'une seule URL à la fois. Le problème était que, une fois instancié, l'AFHTTPClient ne peut pas changer son URL. Donc la solution est d'avoir AFHTTPClient entier comme une propriété privée de mon propre client au lieu de l'étendre. Si l'URL change, je peux juste instancier un nouveau AFHTTPClient dans mon singleton. Ça devrait le faire. – Jan

Répondre

1

Cela pourrait être la solution. Au lieu de sous-classer l'AFHTTPClient, il suffit de le définir comme propriété et de le réinstancier si l'URL change:

#import "FooAPIClient.h" 
#import "AFJSONRequestOperation.h" 
#import "AFHTTPClient.h" 

static NSString * const kFooAPIBaseURLString = @"http://192.168.0.1"; 

@interface FooAPIClient() 
@property AFHTTPClient * httpClient; 
@end 

@implementation FooAPIClient 

+ (instancetype)sharedClient { 
    static FooAPIClient *_sharedClient = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return _sharedClient; 
} 

- (id)initWithBaseURL:(NSURL *)url { 

    self = [super init]; 
    if (!self) { 
     self.httpClient = [self setupClientForURL:url]; 
    } 
    return self; 
} 

-(AFHTTPClient*) setupClientForURL:(NSURL*) url { 
    AFHTTPClient * httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; 
    [httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]]; 
    [httpClient setDefaultHeader:@"Accept" value:@"application/json"]; 
    return httpClient; 
} 

#pragma mark - RemoteConfigurationDelegate 

-(void) apiURLChanged:(NSURL*) newURL { 
    self.httpClient = [self setupClientForURL:newURL]; 
} 


#pragma mark - Public 

-(void) consumeAPI:(CompletionBlock) completion { 
    [self.httpClient getPath:@"foo" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { 
     if(completion) { 
      completion(responseObject, nil); 
     } 
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
     if(completion) { 
      completion(nil, error); 
     } 
    }]; 
} 



@end 
1

Le singleton ne doit pas être un carcan.

Cette leçon que j'ai appris de Cocoa Touch. Il existe plusieurs classes dans le framework qui utilisent des instances partagées, mais vous permettent de créer vos propres instances si vous en avez besoin. NSNumberFormatter, NSDateFormatter, NSBundle, NSFileManager et beaucoup d'autres sont des exemples de classes où vous pouvez créer vos propres instances si vous en avez besoin.

Dans votre cas, je l'aurais deux méthodes de classe qui renvoient des instances:

+ (instancetype)sharedClient { 
    static FooAPIClient *instance = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return instance; 
} 

static FooAPIClient *FooSharedCorporateInstance; 

+ (instancetype)sharedCorporateClient { 
    @syncronized (FooSharedCorporateInstance) { 
     return FooSharedCorporateInstance; 
    } 
} 

+ (void)setSharedCorporateClientWithURL:(NSURL *)URL { 
    @syncronized (FooSharedCorporateInstance) { 
     FooSharedCorporateInstance = [[self alloc] initWithBaseURL:URL]; 
    } 
} 

Comme un avantage secondaire, cette force des responsabilités de classe et d'instance distinctes qui ont tendance à brouiller dans les classes singleton.

+0

Non, ce n'est pas une solution. L'URL ne peut pas être une constante. L'URL sera définie dynamiquement au moment de l'exécution. – Jan

+0

@Jan J'ai mis à jour ma réponse. Vous pouvez appeler '+ setSharedCorporateClientWithURL:' au démarrage avec la valeur initiale ou utiliser nil pour indiquer aucun client d'entreprise. –

+0

avec la solution '+ setSharedCorporateClientWithURL:', les objets qui ont une référence à une ancienne "instance partagée" continueraient à parler à l'ancienne URL. Ce ne serait donc plus un modèle Singleton. – Jan

Questions connexes