af源码 ios
2016-08-18 15:22:00 weixin_33724659 阅读数 9

有没有感觉每次面试都会被问到 ,来小伙,你给我讲一下AF ,百分之八十的面试者 瞬间懵逼.再想一下如果某天 AF消失啦 ,消失啦,消失啦 怎么办,怎么办,怎么办////////////
http://www.qingpingshan.com/rjbc/ios/105767.html
http://www.w2bc.com/Article/13300
http://www.cnblogs.com/polobymulberry/p/5081049.html
http://blog.csdn.net/codywangziham01/article/details/38088017
http://doc.okbase.net/super_man_ww/archive/228130.html
http://blog.csdn.net/heapstackk/article/details/48680869
http://www.cnblogs.com/chenxianming/p/5674652.html
http://blog.sina.com.cn/s/blog_be8740510102xf4h.html
http://blog.csdn.net/wuyouyee/article/details/51066524
一,
在了解AF之前 我们来先考虑一下 如果自己去搞一个网络请求怎么搞.搞定以后再去膜拜大神么写的AFNETworking.

+(void)postByUrlPath:(NSString *)path andParams:(NSString *)params andCallBack:(CallBack)callback{
    
    NSURL *url = [NSURL URLWithString:path];
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    //multipart/form-data
    //application/x-www-form-urlencoded 默认是这个 af用的也是这个格式的
     [request setValue:@"application/json;chatset=utf-8" forHTTPHeaderField:@"Content-Type"];
    NSData *data = [params dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = data;
    [request setValue:[NSString stringWithFormat:@"%ld",data.length] forHTTPHeaderField:@"Content-Length"];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            
            
            NSDictionary* jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:Nil];
            NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"请求结果 ==%@    \\n$$$$ = %@",response,str);
            callback(jsonData);

        });
        
        
    }];
    //开始请求
    [task resume];
}

上面是我自己写的一个post请求
1.首先创建 URL 并且通过URL 创建一个请求 request.
2.接下来创建 请求方式也就是说HTTPMethod = @"POST";
3.接下来创建 请求头[request setValue:@"application/json;chatset=utf-8" forHTTPHeaderField:@"Content-Type"];
4.由于是post请求 需要把参数设置为请求体 并且是这长度

[request setValue:@"application/json;chatset=utf-8" forHTTPHeaderField:@"Content-Type"];
 NSData *data = [params dataUsingEncoding:NSUTF8StringEncoding]; 
request.HTTPBody = data; 
[request setValue:[NSString stringWithFormat:@"%ld",data.length] forHTTPHeaderField:@"Content-Length"];

目前 connect 已经废弃 所以用户session
5.创建session 并且用session 和request 创建数据下载任务

NSURLSession *session = [NSURLSession sharedSession]; 
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{

}

其中data就是返回的数据
二, 有木有感觉很简单 那么af是用来干嘛的 不就是网络请求么 那么他必然逃不了这个过程.
接下来呢 我们先了解一下af的用法通过用法来一步一步去学习啊.

 AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
session.requestSerializer = [AFJSONRequestSerializer serializer];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"start"] = @"1";
params[@"end"] = @"5"; 
[session POST:urlStr parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 
NSLog(@"请求成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 
NSLog(@"请求成功");
}];

1.第一步创建了 一个 session管理者,并且通过管理者设置序列化为json格式的

  1. 创建请求参数字典 ,和URL
  2. 调用post方法 并且得到请求结果以block的方式返回.

对于manager 是干啥的县不解释.
我们在调用post方法时 才传入了URL params,由此我们可以猜到
post方法里面 做了哪些呢 : 创建 request 并且设置请求头 请求方式,请求体. 并且通过session 创建下载任务,将返回的nsdata 返回来.
githup 自己下载一个 af 打开我们看到


1907672-0d2fd71108ea6d8d.png
FB42C317-D504-4AFB-8F13-597FB8131D9F.png

查看 manager

AFHTTPSessionManager *session = [AFHTTPSessionManager manager];

我们可以在相应的文件里面常看 创建manager的过程

1907672-ed9d18877ecc0261.png
5EF74465-C83C-4433-9DDD-70783E30240D.png

可以看到 manager 最终执行的是这个步骤 并且在过程中参数都为nil 似乎看不到里面是什么有用的 ,那么紧接着 我们在看里面每个函数的意思


1907672-c945e28be6072fe8.png
7BC8568F-DAC0-4C57-BA92-0CA4F8E55027.png
1907672-3c194b9002ed6364.png
EADBEAF1-5274-426C-8C8F-A2EE717A6A68.png

接下来主要理解这个方法的内容:
NSURLSessionConfiguration 通过字面理解可以知道这是为了给session 配置(http://blog.sina.com.cn/s/blog_877e9c3c0102uzox.html) 并且开启了队列线程 设置最大为1

接下来就是创建下载任务并且执行下面代码

[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];

从字面意思上看应该是添加代理给task


1907672-e33bc6c768ce8dde.png
A2EED5A0-E1EE-4CA4-A1D9-C85AF90CF159.png

1907672-2fb119ddd31ccce0.png
1665B669-E780-4320-AFFB-39FFAEC60E68.png

通过以上代码 将 下载任务的返回属性 复制给AFURLSessionManagerTaskDelegate 看到这里后 感觉读不下去啦 因为很多不理解的地方(据说af最牛逼和巧妙的地方 就在这里 block 和代理之间的转换 ). 那么返回来接着看

manager 创建过程已经完成.
接下来即使调用 post 创建下载任务开始下载

1907672-9bc8ca48e1f2c61c.png
C29F5309-1DC2-4845-AC02-CEF702E6E780.png

1907672-bc637c3d8a9f3173.png
4156B4A6-20E9-45D3-8F7F-8F809FF34D9C.png

在这里 创建了request 和下载任务 并且反悔了 成功和失败的详情

接下来 看着一句的内容

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
1907672-12b432b26db65ed1.png
04CFA25F-96F0-4AD9-8A96-6AFFFCA88624.png

可以看到 这里设置啦 请求方式 接下来用这个方法给request 进行拉相应的设置


1907672-8d5de89259bd2866.png
6986F714-1117-4C49-9C0F-763DCBEF55E2.png

在这里可以重新设置请求request


1907672-29083ca4b5281c9e.png
DEA1BE7B-BDC4-4DAA-B6DB-1994317A7819.png

接下来就是是datatask啦
 dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) 
1907672-33d0a2e834a980c5.png
33F37966-93C6-473F-87D7-F919E27065F1.png

第一个 红框 用request创建了 下载任务, 第二个红框 看名字addDelegate..... 以为是给datatask 添加代理,事实上 它类似与给一个model赋值,将datatask 和下载进度 以及完成结果赋值给AFURLSessionManagerTaskDelegate这个对象 接着往下看

1907672-19f0d87a4da22f08.png
0048EF9B-B69B-4C02-82FE-6A186CA9EE54.png

可以看到对delegate的各个属性进行了赋值,同事还有一个红色框的函数 setDelagate
这个名字才是真正的给datatask 和delegate进行绑定


1907672-69c9c2ee7c61c721.png
64676409-B70D-40E9-8DFF-A7590A24B0C7.png

第一个红框即使将delegate和和task的标示进行绑定 一task的标示为可以 delegate为值.
第二个红框的函数看名字应该是添加通知或者是kvo之类的东西


1907672-ac0c30ba6e48e420.png
A7A83C22-AF0F-4A19-AAF5-EF363449B3E3.png

可以看出来是注册了通知 一旦接受到task发送来的didresume名字的通知就会执行taskdidiresume 函数
1907672-8d8628de03dd4002.png
73D0F79C-B467-4879-9C07-564FD8727976.png

可以看出来 函数里有发送了一个同名字的通知但是我知道这是什么意思

似乎读到这了 不知道该怎么办啦 这时候我们字啊看一下 一开始我们的manager 他是


1907672-9f44ce4490783a58.png
8AE35A42-DBC7-41EF-97CB-39632EE33CB0.png

可以看出来他是继承与AFURLSessionManager 那我们再去看AFURLSessionManager


1907672-dd01e21bd06d23bf.png
B79F5955-6F35-4BB8-A4F3-5872994B711A.png

可以看出来他遵守了一系列的协议
我们看一个NSURLSessionTaskDelegate的代理方法


1907672-49ee3603caea6365.png
7D1F7979-2ECC-4B44-A8E2-93E3AC389388.png

这个代理是在已经完成下载任务的代理 有任务和错误等参数,也就是说nsurlsession 下载任务完成以后会自动调用这个任务并且将task 得到.也就是常说的,iOS原生的网络结果是通过代理返回的,而 AF=通过这个model将delegate的返回值,转换成block.接着往下看,


1907672-b879fee785b74f22.png
8C670B95-B48E-4340-8CB7-362DA55339C8.png

通过字典的task的唯一标示获取了一开始和task绑定好的得了delegate model,然后通过delegate调用了
[delegate URLSession:session task:task didCompleteWithError:error];
看到这个是不是懵逼啦,这个函数不就是本身么.怎么调用 ,我在看是啥调用的是delegate调用的 别那么自然我们去delegate里面看


1907672-bd8be43be3da4943.png
538C22E0-8BA6-4EE3-8108-225AD2A7D253.png

可以看出来他也准守了这三个代理接下来看调用的函数
1907672-171f41344b397387.png
114F49C1-9500-4C43-A108-B463447C4A9E.png
1907672-293f65dc695fe034.png
6D1B4CA5-4E85-44D9-9801-71FED830D17A.png

可以看出来在这里 分为有错误和无错误两种情况将结果以block的方式返回给了用户.当然里面还有一系列的结果序列化,这里就不看啦.
这是个人门前对af的一个简单的阅读思路吧,接下来有时间再具体看一下

2016-08-14 21:52:00 weixin_34365417 阅读数 14

AF带来的血案

51735-96169f62180c94aa.png
51735-3aa964d425fa1f2e.png
form-data.png

已上图是AF源码截图,基于表单的形式上传。使用的是multipart/form-data发送文件。
Content-Type必须是multipart/form-data

原理分析

以multipart/form-data编码的POST请求格式与application/x-www-form-urlencoded完全不同.

其中application/x-www-form-urlencoded提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。然而multipart/form-data是这样的形式:

key1 = val1

key2 = vals

关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。http://www.ietf.org/rfc/rfc1867.txt

例子

<pre>
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=XXX
此处有有空格*
--XXX
Content-Disposition: form-data; name="text"

title
--XXX
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
此处有有空格*
PNG ... content of chrome.png ...
--XXX

</pre>

例子说明

  • 首先生成了一个 boundary=XXX 用于分割不同的字段,为了避免与正文内容重复,boundary 可能会很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码。

  • 消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。

  • 如果传输的是文件,还要包含文件名和文件类型信息。

  • 消息主体最后以 --boundary-- 标示结束。

iOS多图片上传

<pre>

/**

  • 上传图片
  • @param operations 上传图片等预留参数---视具体情况而定 可移除
  • @param imageArray 上传的图片数组
  • @parm width 图片要被压缩到的宽度
  • @param urlString 上传的url---请填写完整的url
  • @param successBlock 上传成功的回调
  • @param failureBlock 上传失败的回调
  • @param progress 上传进度

*/
+(void)uploadImageWithOperations:(NSDictionary *)operations withImageArray:(NSArray *)imageArray withtargetWidth:(CGFloat )width withUrlString:(NSString *)urlString withSuccessBlock:(requestSuccess)successBlock withFailurBlock:(requestFailure)failureBlock withUpLoadProgress:(uploadProgress)progress;
{

//1.创建管理者对象

  AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

 [manager POST:urlString parameters:operations constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
    
    NSUInteger i = 0 ;
    
    /**出于性能考虑,将上传图片进行压缩*/
    for (UIImage * image in imageArray) {
        
        //image的分类方法
        UIImage *  resizedImage =  [UIImage IMGCompressed:image targetWidth:width];
        
        NSData * imgData = UIImageJPEGRepresentation(resizedImage, .5);
        
        //拼接data
        [formData appendPartWithFileData:imgData name:[NSString stringWithFormat:@"picflie%ld",(long)i] fileName:@"image.png" mimeType:@" image/jpeg"];
        
        i++;
    }
    
} progress:^(NSProgress * _Nonnull uploadProgress) {
    
    progress(uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
    
} success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {
    
    successBlock(responseObject);
    
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
   
    failureBlock(error);
    
}];

}

</pre>

扩展

四种常见的 POST 提交数据方式:以上两种 + application/json + text/xml

参考

1.http://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data

2.https://imququ.com/post/four-ways-to-post-data-in-http.html#toc-2

2016-08-14 21:52:00 weixin_34026276 阅读数 6

AF带来的血案

51735-96169f62180c94aa.png
51735-3aa964d425fa1f2e.png
form-data.png

已上图是AF源码截图,基于表单的形式上传。使用的是multipart/form-data发送文件。
Content-Type必须是multipart/form-data

原理分析

以multipart/form-data编码的POST请求格式与application/x-www-form-urlencoded完全不同.

其中application/x-www-form-urlencoded提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。然而multipart/form-data是这样的形式:

key1 = val1

key2 = vals

关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。http://www.ietf.org/rfc/rfc1867.txt

例子

<pre>
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=XXX
此处有有空格*
--XXX
Content-Disposition: form-data; name="text"

title
--XXX
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
此处有有空格*
PNG ... content of chrome.png ...
--XXX

</pre>

例子说明

  • 首先生成了一个 boundary=XXX 用于分割不同的字段,为了避免与正文内容重复,boundary 可能会很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码。

  • 消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。

  • 如果传输的是文件,还要包含文件名和文件类型信息。

  • 消息主体最后以 --boundary-- 标示结束。

iOS多图片上传

<pre>

/**

  • 上传图片
  • @param operations 上传图片等预留参数---视具体情况而定 可移除
  • @param imageArray 上传的图片数组
  • @parm width 图片要被压缩到的宽度
  • @param urlString 上传的url---请填写完整的url
  • @param successBlock 上传成功的回调
  • @param failureBlock 上传失败的回调
  • @param progress 上传进度

*/
+(void)uploadImageWithOperations:(NSDictionary *)operations withImageArray:(NSArray *)imageArray withtargetWidth:(CGFloat )width withUrlString:(NSString *)urlString withSuccessBlock:(requestSuccess)successBlock withFailurBlock:(requestFailure)failureBlock withUpLoadProgress:(uploadProgress)progress;
{

//1.创建管理者对象

  AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

 [manager POST:urlString parameters:operations constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
    
    NSUInteger i = 0 ;
    
    /**出于性能考虑,将上传图片进行压缩*/
    for (UIImage * image in imageArray) {
        
        //image的分类方法
        UIImage *  resizedImage =  [UIImage IMGCompressed:image targetWidth:width];
        
        NSData * imgData = UIImageJPEGRepresentation(resizedImage, .5);
        
        //拼接data
        [formData appendPartWithFileData:imgData name:[NSString stringWithFormat:@"picflie%ld",(long)i] fileName:@"image.png" mimeType:@" image/jpeg"];
        
        i++;
    }
    
} progress:^(NSProgress * _Nonnull uploadProgress) {
    
    progress(uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
    
} success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {
    
    successBlock(responseObject);
    
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
   
    failureBlock(error);
    
}];

}

</pre>

扩展

四种常见的 POST 提交数据方式:以上两种 + application/json + text/xml

参考

1.http://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data

2.https://imququ.com/post/four-ways-to-post-data-in-http.html#toc-2

转载于:https://www.jianshu.com/p/3aaf3d4b0b35

2017-08-10 11:48:00 weixin_33672109 阅读数 11

最近在弄高德地图,在高德API里面有一个定位功能获取经纬度,我正常使用AFNetWork 进行网络请求的时候 报错:

invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_Run

解决办法是后来发现,AF3.0以上要对中文参数进行转码,很无语 因为之前也有传中文参数,都没有进行转码操作, 用了一下高德就要转吗   转码方法如下

NSString*str=[NSStringstringWithFormat:@"https://alpha-api.app.net/stream/0/posts/stream/global"];

NSURL*url = [NSURLURLWithString:[strstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

最后你把这个url放到需要请求的URL 里面 就可以了

希望对有需要的小伙伴有帮助

2017-06-03 13:02:09 zuojinmin 阅读数 344

    [[AFHTTPSessionManager manager]POST:@"http://localhost:8080/file" parameters:@{@"name":@"haha",@"email":@"312093933@qq.com"} constructingBodyWithBlock:^(id<AFMultipartFormData_Nonnull formData) {

        

        

        

        NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"1.jpg" withExtension:nil];

        

        // 要上传保存在服务器中的名称

        // 使用时间来作为文件名 2014-04-30 14:20:57.png

        // 让不同的用户信息,保存在不同目录中

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

        // 设置日期格式

        formatter.dateFormat = @"yyyy-MM-ddHH:mm:ss";

        NSString *fileName = [formatter stringFromDate:[NSDate date]];

        

        [formData appendPartWithFileURL:fileURL name:@"file" fileName:[NSString stringWithFormat:@"%@%@",fileName,@".jpg"] mimeType:@"image/png" error:NULL];

        

        

    } progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

        

        NSLog(@"task header====%@",task.currentRequest.allHTTPHeaderFields);

        

        

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        

        

        

    }];

没有更多推荐了,返回首页