9

在我的 iPhone 应用程序中,我使用 AFHTTPClient 的子类来访问一个 REST Web 服务。我希望我的所有请求都由我的 API 客户端的一个实例处理,所以我使用单例模式。

当服务仅在一次 URL 上运行时,这可以正常工作。我可以使用一个常量值来设置 URL。

现在,在应用程序的最终版本中,每个应用程序实际上将与将安装在公司网络中的另一个服务通信。

所以我将从远程配置中获取服务 URL。单例模式在这里仍然是一个不错的选择吗?如果 URL 在应用程序运行时甚至可以更改,我应该如何对其进行参数化?

干杯

#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
4

2 回答 2

1

这可能是解决方案。无需继承 AFHTTPClient,只需将其设置为属性并在 URL 更改时重新实例化它:

#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
于 2013-09-03T20:27:31.207 回答
1

单例模式不必是紧身衣。

我从 Cocoa Touch 学到的这一课。框架中有几个使用共享实例的类,但如果需要,您可以灵活地创建自己的实例。NSNumberFormatter、NSDateFormatter、NSBundle、NSFileManager 和许多其他类都是类的示例,如果需要,您可以在其中创建自己的实例。

在您的情况下,我将有两个返回实例的类方法:

+ (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];
   }
}

作为一个附带的好处,这会强制分离类和实例责任,这在单例类中往往会变得模糊。

于 2013-09-03T19:59:37.540 回答