0

我对 Objective-C 和 iOS 开发有点陌生(使用它大约 1.5 年,实际上只是最近 8 个月左右才开始大量参与)。我编写了一个自定义类来处理我所有的网络服务请求。我使用AFNetworking处理这些请求(并且喜欢它),但我想确保我所做的事情是高效的,并且不会在以后引起问题。

从我在 Instruments 中看到的以及应用程序的性能来看,这似乎是一个很好的方法,但我真的远非专家,我希望得到一些反馈和/或建议。

这是我的 NetworkClient 类:

网络客户端.h:

#import <Foundation/Foundation.h>

extern NSString * const APIKey;

@interface NetworkClient : NSObject

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                          block:(void (^)(id obj))block;

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
                          block:(void (^)(id obj))block;

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
             alertUserOnFailure:(BOOL)alertUserOnFailure
                          block:(void (^)(id obj))block;

+(void)handleNetworkErrorWithError:(NSError *)error;

+(void)handleNoAccessWithReason:(NSString *)reason;

@end

网络客户端.m:

#import "NetworkClient.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "SBJson.h"

NSString * const APIKey = @"MyAPIKeyThatIsDefinedInDatabasePerApplication";

@implementation NetworkClient

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                          block:(void (^)(id obj))block {

    [self processURLRequestWithURL:url andParams:params syncRequest:NO alertUserOnFailure:NO block:^(id obj) {
        block(obj);
    }];
}

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
                          block:(void (^)(id obj))block {
    [self processURLRequestWithURL:url andParams:params syncRequest:syncRequest alertUserOnFailure:NO block:^(id obj) {
        block(obj);
    }];
}


+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
             alertUserOnFailure:(BOOL)alertUserOnFailure
                          block:(void (^)(id obj))block {

    // Default url goes here, pass in a nil to use it
    if (url == nil) {
        url = @"https://MyURLToWebService";
    }

    // Add in our API Key
    NSMutableDictionary *newParams = [[NSMutableDictionary alloc] initWithDictionary:params];
    [newParams setValue:APIKey forKey:@"APIKey"];

    NSURL *requestURL;
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:requestURL];

    NSMutableURLRequest *theRequest = [httpClient requestWithMethod:@"POST" path:url parameters:newParams];

    __block NSString *responseString = @"";

    AFHTTPRequestOperation *_operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
    __weak AFHTTPRequestOperation *operation = _operation;

    [operation  setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        responseString = [operation responseString];

        id retObj = [responseString JSONValue];

        // Check for invalid response (No Access)
        if ([retObj isKindOfClass:[NSDictionary class]]) {
            if ([[(NSDictionary *)retObj valueForKey:@"Message"] isEqualToString:@"No Access"]) {
                block(nil);
                [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]];
            }
        } else if ([retObj isKindOfClass:[NSArray class]]) {
            if ([(NSArray *)retObj count] > 0) {
                NSDictionary *dict = [(NSArray *)retObj objectAtIndex:0];
                if ([[dict valueForKey:@"Message"] isEqualToString:@"No Access"]) {
                    block(nil);
                    [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]];
                }
            }
        }
        block(retObj);
    } 
                                      failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                          NSLog(@"Failed with error = %@", [NSString stringWithFormat:@"[Error]:%@",error]);
                                          block(nil);
                                          if (alertUserOnFailure) {
                                              // Let the user know something went wrong
                                              [self handleNetworkErrorWithError:operation.error];
                                          }

                                      }];

    [operation start];

    if (syncRequest) {
        // Process the request syncronously
        [operation waitUntilFinished];
    } 


}


+(void)handleNetworkErrorWithError:(NSError *)error {
    NSString *errorString = [NSString stringWithFormat:@"[Error]:%@",error];

    // Standard UIAlert Syntax
    UIAlertView *myAlert = [[UIAlertView alloc] 
                            initWithTitle:@"Connection Error" 
                            message:errorString 
                            delegate:nil 
                            cancelButtonTitle:@"OK" 
                            otherButtonTitles:nil, nil];

    [myAlert show];

}

+(void)handleNoAccessWithReason:(NSString *)reason {
    // Standard UIAlert Syntax
    UIAlertView *myAlert = [[UIAlertView alloc] 
                            initWithTitle:@"No Access" 
                            message:reason 
                            delegate:nil 
                            cancelButtonTitle:@"OK" 
                            otherButtonTitles:nil, nil];

    [myAlert show];

}

@end

这就是我所说的:

NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
                            @"GetApplications", @"Command",
                            userInfo.networkID, @"NetworkID",
                            nil];

    [NetworkClient processURLRequestWithURL:nil andParams:params block:^(id obj) {
        [MBProgressHUD hideHUDForView:self.view animated:YES];

        if ([obj isKindOfClass:[NSArray class]]) {
            myTableViewData = (NSArray *)obj;        
            [self.myTableView reloadData];  
        }
    }];

所以我的网络服务可以发送回一个字典结构的 JSON 响应和一个数组格式的 JSON 响应。NetworkClient 方法将同时接收并发送回它所获得的内容(我将其留给调用代码以确保它返回预期的内容)。我使用 APIKey 作为额外的安全措施,以确保只有我的应用程序可以访问 Web 服务资源(在发回数据之前我检查的第一件事是 APIKey 与我在数据库中为该应用程序所拥有的匹配)。

这是做这种事情的有效方法吗?有什么办法让它变得更好吗?

4

1 回答 1

3

我不明白您为什么要使用它processURLRequestWithURL:nil,因为这是为了处理特定服务。它也很混乱,它只告诉我在别处有一些魔法,就好像它根本不存在一样。我会使用单例:

extern NSString * const kBaseURL;

@interface NetworkClient : AFHTTPClient
+ (NetworkClient *) sharedClient;
@end

NSString* const kNodeApiURL = BASE_URL;

@implementation NetworkClient

+ (NetworkClient*) sharedClient
{
    static NetworkClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = [[NetworkClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
    });
    return _sharedClient;
}

- (id)initWithBaseURL:(NSURL*)url 
{
    if (self = [super initWithBaseURL:url]) {
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
        [self setDefaultHeader:@"Accept" value:@"application/json"];
    }
    return self;
}

-(id) init {
    error(@"Use initWithBaseURL: instead.");
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

@end

然后在 PCH 上

#define BASE_URL  @"https://MyURLToWebService"

当您添加参数以显示弹出窗口时,您还将 GUI 与服务器 API 混合。我不认为服务器 API 应该阻塞线程。编写纯异步代码,让调用者从他自己的一端阻止 GUI。

与 handleNoAccessWithReason 相同。API 不处理任何事情,它吸收输入并产生输出。您编写的每一段代码都应该做一 (1) 件事。它将更容易测试、理解和重用。

我不知道你为什么用 __weak 来限定操作。

你传递的那些参数,如果你使用像 User 和 Command 之类的域对象,它会更容易理解。好吧,“命令”很臭。在你的使用代码后面真的有一个名字有意义的方法吗?因为当我调试代码时,我必须打印参数来告诉我发生了什么,我很生气。如果您正在编写服务器 API(如果您想要干净的代码,则默认情况下您应该这样做),您应该公开有意义的名称。

我会以不同的方式编写代码,例如,我想为给定用户从服务器获取一头牛:

typedef void (^AFJSONSuccess)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON);
typedef void (^AFJSONFailure)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON);

+(void) cowForUser:(User*)user callback:(void(^)(Cow *cow, NSError *error))callback {

    AFJSONSuccess success = ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        // turn JSON into a cow
        callback(cow,nil);
    };

    AFJSONFailure failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        // create a custom NSError
        callback(nil,error);
    };

    NetworkClient *client = [NetworkClient sharedClient];
    NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:kCowPath parameters:jsonDic];
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:sucess failure:failure];
    [client enqueueHTTPRequestOperation:operation];
}

现在在使用中启动 hud,并在回调块中调用 stop hud 并检查牛是否为 nil。我不认为 hud 应该阻塞屏幕(就像你等到操作完成一样),如果用户决定移动到另一个屏幕或取消查询怎么办?

我投票结束这个问题,因为它属于代码审查。

于 2012-08-21T22:45:01.550 回答