下objc_msgSend包装AFN请求

2016.5.10 Update

下述的代码在模拟器运行无问题,但是在64员真机上有了EXC_BAD_ACCESS问题。
马上是以规定得优先定义原型才能够动用(原文见附1
苹果为起如下示例

- (int) doSomething:(int) x { ... }
- (void) doSomethingElse {
    int (*action)(id, SEL, int) = (int (*)(id, SEL, int)) objc_msgSend;
    action(self, @selector(doSomething:), 0);
}

故此对于返回值为void的下式

objc_msgSend(manager, NSSelectorFromString(selecterStr), url, parmas, nil, successBlock, failureBlock);

咱们以之替换为

((void(*)(id, SEL, NSString *, NSDictionary *, void(^successBlock)(NSURLSessionDataTask * _Nonnull, id _Nullable), void(^failureBlock)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull)))objc_msgSend)(manager, NSSelectorFromString(selectorStr), url, parmas, successBlock, failureBlock);

看起来比辛苦,不过会缓解问题。

正文

就年头,啥还尊重个次浅包装,为了以后底版本迁移/代码库更新/重构简化/三正升迁。
科学,我啊这样做了。
每当一个XXRequest的好像中,二不善封装了AFNetworking网络请求。


包装了后意识了一些题材…
后台受回复的接口有部分凡是因此Get请求的,有有凡用Post请求的。
通App并无涉及到直达污染图片或文件的效用。
从而出现了平等截还的代码。

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
if (type == XXRequestTypeGet) {
    [manager GET:url parameters:parmas progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       // 请求成功后的数据处理
       // 隐藏hud、回调Block等
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
           // 请求失败的数据处理
    }];
} else {
    [manager POST:url parameters:parmas progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       // 请求成功后的数据处理
       // 隐藏hud、回调Block等
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       // 请求失败的数据处理
    }];

其中type参数是外界传进判断请求类型的枚举。
达到段遭遇蕴藏了少数有的肉眼可见的再代码。
当时段更的代码让丁进一步看更不爽…

故此尝试采取runtime,动态调用来加载请求。


动态调用最直接想到的法门自然是下就三单方法..

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

遗憾之凡会传递的参数个数并无入我们的需求…

因而更朝着生转一层,使用objc_msgSend()来实现。
行使该措施首先得导入<objc/message.h>包。
实际实现代码如下:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
void(^successBlock)(NSURLSessionDataTask * _Nonnull, id _Nullable) = ^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
   // 请求成功后的数据处理
   // 隐藏hud、回调Block等
};
void(^failureBlock)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull) = ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
   // 请求失败的数据处理
};
NSString *selecterType = type == XXRequestTypeGet ? @"GET" : @"POST";
NSString *selecterStr = [NSString stringWithFormat:@"%@:parameters:progress:success:failure:", selecterType]
objc_msgSend(manager, NSSelectorFromString(selecterStr), url, parmas, nil, successBlock, failureBlock);

代码写了了,Ctrl+B编译…然后虽报错了。

879.jpg

源于出自于LLVM的严酷检查。
解决办法就是关掉它。
关闭措施:

1. 点击项目
2. 点击 Build Settings
3. 在 Basic/All 中选择All
4. 搜索 objc
5. 找到 Apple LLVM x.x - Preprocessing 分组
6. 把 Enable Strict Checking of objc_msgSend Calls 后面的值由 YES 修改为 NO

复编译,没有报错了。

总结:
个别种方法动态调用方法

  1. performSelector
    一直调用,传参个数限制于3独里头。
  2. objc_msgSend
    内需导入message.h头文件,传参个数不限。

附1

Dispatch Objective-C Messages Using the Method Function’s Prototype
An exception to the casting rule described above is when you are
calling the objc_msgSend function or any other similar functions in
the Objective-C runtime that send messages. Although the prototype for
the message functions has a variadic form, the method function that is
called by the Objective-C runtime does not share the same prototype.
The Objective-C runtime directly dispatches to the function that
implements the method, so the calling conventions are mismatched, as
described previously. Therefore you must cast the objc_msgSend
function to a prototype that matches the method function being
called.
Listing 2-14 shows the proper form for dispatching a message to an
object using the low-level message functions. In this example, the
doSomething: method takes a single parameter and does not have a
variadic form. It casts the objc_msgSend function using the prototype
of the method function. Note that a method function always takes an id
variable and a selector as its first two parameters. After the
objc_msgSend function is cast to a function pointer, the call is
dispatched through that same function pointer

相关文章