msgSend在开发中的应用

正常情况下,我们会通过代码显性调用对应的方法实现,在底层通过消息机制来完成消息的发送,实现方法调用。但是有些情况下,我们通过直接调用msgSend方法可以极大地简化实现,节约开发时间.下面我们举两个简单的例子来进行说明.

统一处理不同方式的请求

AFNetworking是我们经常使用的一个网络请求第三方,由于该类库对于请求方式进行了分开处理,所以如果请求中有多种请求方式并存我们就需要对不同的方式进行分开处理,但是如果使用msgSend来发送消息就可以将多种方式和在一起进行统一处理.

仔细观察一下AFHTTPSessionManager中常用的几种不同请求方式的api:

//GET
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;

//POST
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;

//PUT
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//PATCH
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//DELETE
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                      parameters:(nullable id)parameters
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

除了请求方式不同之外,其余的方法体部分部分全部相同,将不同的部分摘出来单独处理,使用字符串进行拼接然后转化为对应的SEL即可.

- (AFHTTPSessionManager *)manager {
    if (!_manager) {
        _manager = [AFHTTPSessionManager manager];
        _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        //code here for other settingd 
    
    }
    return _manager;
    
}
- (void)loadRequest:(NSString * _Nonnull)url paramters:(id)parmas method:(NSString * _Nonnull)method result:(void(^ _Nullable)(id, NSError *))result  {
    
    NSAssert(method, @"method不能为空!");
    NSString *requestMethod = [method uppercaseString];
    
    static NSSet *methods;
    if (methods) {
       methods = [NSSet setWithObjects:@"GET", @"POST", @"PUT", @"DELETE", @"PATCH", nil]
    }
    NSAssert([methods containsObject:requestMethod], @"不支持的请求方式");
    typedef void(^SuccessBlock)(NSURLSessionDataTask * _Nonnull , id  _Nullable );
    typedef void(^FailBlock)(NSURLSessionDataTask * _Nullable , NSError * _Nonnull);
    SuccessBlock successBlock = ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (result) {
            result(responseObject, nil);
        }
    };
    FailBlock failBlock = ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)  {
        if (result) {
            result(nil, error);
        }
    };
    NSString *selectorName = [NSString stringWithFormat:@"%@:parameters:success:failure:", requestMethod];
    SEL sel = NSSelectorFromString(selectorName);
    if ([self.manager respondsToSelector:sel]) {
        ((void(*)(id, SEL, NSString *, id, SuccessBlock, FailBlock))objc_msgSend)(self.manager, sel, url, parmas, successBlock, failBlock);
    } else {
        NSLog(@"不支持此方法!");
        
    }

}

考虑到GET和POST在AFN版本(实例中使用的是3.2.1版本)的中增加了progress参数,

//新的GET方式api
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//新的POST方式api
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

且已经废弃原始的请求的方法,为防止某个版本中彻底废弃不再支持该方法,同时兼容其他几种请求方式不携带progress参数使用api,可以尝试优先调用包含progress参数的方法,如果不响应再调用不懈怠progress参数的方法:

- (void)loadRequest:(NSString * _Nonnull)url paramters:(id)parmas method:(NSString * _Nonnull)method result:(void(^ _Nullable)(id, NSError *))result  {
    NSAssert(method, @"method不能为空!");
    NSString *requestMethod = [method uppercaseString];
    
    static NSSet *methods;
    if (methods) {
        methods = [NSSet setWithObjects:@"GET", @"POST", @"PUT", @"DELETE", @"PATCH", nil];
    }
    NSAssert([methods containsObject:requestMethod], @"不支持的请求方式");
    typedef void(^SuccessBlock)(NSURLSessionDataTask * _Nonnull , id  _Nullable );
    typedef void(^FailBlock)(NSURLSessionDataTask * _Nullable , NSError * _Nonnull);
    typedef void(^ProgressBlock)(NSProgress * _Nonnull);
    SuccessBlock successBlock = ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (result) {
            result(responseObject, nil);
        }
    };
    ProgressBlock progressBlock = ^(NSProgress * _Nonnull downloadProgress){
    };
    FailBlock failBlock = ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)  {
        if (result) {
            result(nil, error);
        }
    };
    NSString *selectorName = [NSString stringWithFormat:@"%@:parameters:progress:success:failure:", requestMethod];;
    SEL sel = NSSelectorFromString(selectorName);
    if ([self.manager respondsToSelector:sel]) {
        ((void(*)(id, SEL, NSString *, id, ProgressBlock, SuccessBlock, FailBlock))objc_msgSend)(self.manager, sel, url, parmas, progressBlock,successBlock, failBlock);
    } else {
        selectorName = [NSString stringWithFormat:@"%@:parameters:success:failure:", requestMethod];
        sel = NSSelectorFromString(selectorName);
        BOOL responseToSelector = [self.manager respondsToSelector:sel];
        NSAssert(responseToSelector, @"不支持方法");
        if (responseToSelector) {
            ((void(*)(id, SEL, NSString *, id, SuccessBlock, FailBlock))objc_msgSend)(self.manager, sel, url, parmas, successBlock, failBlock);
        } 
    }
}

这样就可以优先调用带有progress回调的方法,如果不响应就继续尝试调用不带有progress回调的方法,这样就可以同时兼顾GET,POST新的api,又可以兼顾其他几种方式不携带progress回调的api.

统一处理不同的类方法调用

在面向对象的一些类中,使用了类似于工厂模式创建统一的基类方法,然后让各个子类去具体实现多态特性,如果我们需要根据不同的类来调用不同的方法.如果我们需要在不同状态下,使用不同的类进行调用,但是又不想针对不同的类单独编写调用实现,该怎么办呢?

这次以FontAwesomeKit为例.在这个体系中,FAKFontAwesome,FAKFoundationIcons,FAKIonIcons,FAKMaterialIcons,FAKOcticons,FAKZocial都继承自FAKIcon,同时FAKIcon中提供了基类必须实现的方法列表.现在的需求是使用同一套代码来获取不同子类中图标对象.

如果直接使用方法调用,我们就需要根据不同的类来做不同的实现

    FAKIcon *icon = nil;
    if ([className isEqualToString:@"FAKFontAwesome"]) {
        icon = [FAKFontAwesome iconWithCode:fontCode size:size];
    } else if ([className isEqualToString:@"FAKFoundationIcons"]){
        icon = [FAKFoundationIcons iconWithCode:fontCode size:size];
    } else if ([className isEqualToString:@"FAKIonIcons"]) {
        icon = [FAKIonIcons iconWithCode:fontCode size:size];
    } else if ([className isEqualToString:@"FAKMaterialIcons"]) {
        icon = [FAKMaterialIcons iconWithCode:fontCode size:size];
    } else if ([className isEqualToString:@"FAKOcticons"]) {
        icon = [FAKOcticons iconWithCode:fontCode size:size];
    } else if ([className isEqualToString:@"FAKZocial"]) {
        icon = [FAKZocial iconWithCode:fontCode size:size];
    }
...

如果有更多的子类,你可能需要写更多这样的判断结构,没有多大实际意义的重复意义的实现代码,却不得不写,如果使用msgSend,就可以简化这一过程:

    FAKIcon *icon = nil;
    Class class = NSClassFromString(className);
    SEL sel = @selector(iconWithCode:size:);
    if ([object_getClass(class) instancesRespondToSelector:sel]) {
        ((FAKIcon *(*)(id, SEL, NSString *, CGFloat))objc_msgSend)(class, sel, fontCode, size);
    }

这样就可以大大节约时间,避免无用代码的书写,同时使代码结构更加清晰易读.

OK,打完收工,欢迎留言交流.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值