同步请求和异步请求
- 同步请求:阻塞式请求,会导致用户体验的中断
- 异步请求:非阻塞式请求,不中断用户体验,百度地图,百度建议等都使用了异步请求来获取数据,不用中断用户操作。
- NSURL
- +URLWithString:类方法:用字符串创建统一资源定位符对象
NSURLRequest / NSMutaleURLRequest - +requestWithURL:类方法:用统一资源定位符对象创建请求
- +requestWithURL:cachePolicy:timeoutInterval:类方法:用统一资源定位符创建请求并指定缓存策略和超时时间
- -HTTPBody属性:设置请求消息体
- -HTTPBodyStream属性:指定请求消息体的输入流
- -HTTPMethod属性:设置请求方法(最常用的是GET或POST)
- -setValue:forHTTPHeaderField:方法:设置请求头
- NSURLConnection
- +connectionWithRequest:delegate:类方法:创建连接对象并指定委托
- -start / -cancel方法:启动/取消异步请求
- +sendSynchronousRequest:returningResponse:error:类方法:发送同步请求,第一个参数是请求对象,第二个参数是NSURLResponse对象的二重指针,第三个参数是NSError对象的二重指针,返回NSData对象代表从服务器获得的数据。
- +sendAsynchronousRequest:queue:completionHandler:类方法:发送异步请求,第一个参数是请求对象,第二个参数是NSOperationQueue对象(管理各种操作的队列,操作除非完成或取消否则一直由队列对其进行管理),第三参数是用block语法给出的回调代码(当服务器响应完成时进行回调)。该方法返回类型是void,因为不需要等待获得服务器响应的数据,当服务器返回数据完成或请求发生错误或超时时,会执行相应的回调。
- NSURLConnectionDataDelegate
- connection:willSendRequest:redirectResponse:方法:处理重定向的回调方法
- connection:didReceiveResponse:方法:当前连接接收到足够的数据来构造响应对象时回调此方法
- connection:didReceiveData:方法收到服务器数据的回调方法,该方法可能被调用多次,需要进行数据的拼接
- connection:needNewBodyStream:方法:在重定向时需要提供新的消息体时的回调方法
- connection:willCacheResponse:方法:缓存响应时的回调方法
- connectionDidFinishLoading:方法:当前连接数据加载完成时的回调方法
- connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:方法:POST请求上传文件时会回调此方法,该方法用于估计上传的进度
- JSON的由来
JSON全称JavaScript Object Notation,JavaScript中创建对象的表达式,广泛的应用于系统之间的数据交换(尤其是异构系统)。
2. JSON和XML的比较
XML全称eXtensible Markup Language,是通过自定义的扩展标签来保存数据,是曾经异构系统之间交换数据的事实标准,也被很多系统用于保存配置信息(如plist文件),但是目前这两项功能都几乎被JSON取代。个人认为XML仅存的优势是数据可读性更强,而且可以通过DTD和Schema的方式对整个XML进行约束和验证。
- JSON的创建和解析
NSJSONSerialization类
- +JSONObjectWithData:options:error:类方法:将JSON格式的NSData转成对象(通常是数组或字典)
- +JSONObjectWithStream:options:error类方法:将JSON格式的NSInputStream转成对象(通常是数组或字典)
- SDWebImage异步图片下载
- SDWebImage的作用:
- 对UIImageView进行扩展以支持基于Web的图像和缓存管理
- 异步图片下载/加载
- 自动缓存图像超时管理
- 支持GIF/WebP格式
- 背景图压缩
- 相同URL的图像不会被下载两次
- 无效的地址不会多次重试
- 主线程不会被阻塞
- 使用GCD和block,拥有更好的性能
- SDWebImage用法示例:
// 通过SDWebImage对UIImage的扩展方法sd_animatedGIFNamed:加载GIF图片
// 注意:新版本的SDWebImage的方法都加上了sd_前缀作为标识与之前的版本有所不同
// 指定GIF图片名称时不能加后缀.gif否则无法呈现动画效果
UIImage *image = [UIImage sd_animatedGIFNamed:@"gourdboy"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(5, 50, 370, 300);
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
- 用SDWebImage下载图像并显示进度
-(void)viewDidLoad {
[super viewDidLoad];
UIProgressView *progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
progressView.frame = CGRectMake(10, 50, 300, 100);
progressView.tintColor = [UIColor blueColor];
progressView.trackTintColor = [UIColor yellowColor];
[self.view addSubview:progressView];
NSURL *imageUrl = [NSURL URLWithString:@"http://www.baidu.com/img/bd_logo1.png"];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageUrl options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"%f", receivedSize / (double) expectedSize);
progressView.progress = (double) receivedSize / expectedSize;
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(10, 200, 350, 300);
[self.view addSubview:imageView];
}
}
];
}```
- NSURLSession的使用
2013的WWDC上,苹果推出了NSURLConnection的继任者:NSURLSession。与NSURLConnection相比,NSURLsession最直接的改进就是可以配置每个session的缓存、协议、cookie以及证书策略(credential policy),甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个NSURLSession对象都由一个NSURLSessionConfiguration对象来进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项。
NSURLSession中另一大块就是会话任务(NSURLSessionTask),它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask与NSURLConnection最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的task共享其创造者NSURLSession这一公共委托者(common delegate)。
- NSURLSession
- +sessionWithConfiguration:类方法:用指定的配置创建会话对象
- +sessionWithConfiguration:delegate:delegateQueue:类方法:用指定的配置、委托和操作队列创建会话
- +sharedSession:类方法:使用默认配置获得会话的单例
- -dataTaskWithURL:方法:用指定的统一资源定位符创建GET请求
- -dataTaskWithURL:completionHandler:方法:用指定的统一资源定位符创建GET请求并用Block指定请求完成的回调代码
- -dataTaskWithRequest:方法:用指定的请求对象创建一个请求
- -dataTaskWithRequest:completionHandler:方法:用指定的请求对象创建一个请求并用Block指定请求完成的回调代码
- -downloadTaskWithURL:方法:用指定的统一资源定位符创建一个下载任务
- -downloadTaskWithURL:completionHandler:方法:用指定的统一资源定位符创建一个下载任务并用Block指定请求完成的回调代码
- -downloadTaskWithRequest:方法:用指定的请求对象创建一个下载任务
- -downloadTaskWithRequest:completionHandler:方法:用指定的请求对象创建一个下载任务并用指定的请求对象创建一个下载任务
- -uploadTaskWithRequest:fromData:方法:用指定的请求和数据创建一个上传任务
- -uploadTaskWithRequest:fromData:completionHandler:方法:用指定的请求和数据创建一个上传任务并用Block指定请求完成的回调代码
- -uploadTaskWithRequest:fromFile:方法:用指定请求和文件创建一个上传任务
- -uploadTaskWithRequest:fromFile:completionHandler:方法:用指定请求和文件创建一个上传任务并用Block指定请求完成的回调代码
- NSURLSessionConfiguration
NSURLSessionConfiguration有三个类工厂方法,这很好地说明了 NSURLSession设计时所考虑的不同的使用场景。
- +defaultSessionConfiguration方法:返回一个标准的配置,这个配置实际上与NSURLConnection具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享 NSURLCredentialStorage。
- +ephemeralSessionConfiguration方法:返回一个预设配置,这个配置中不会对缓存,Cookie和证书进行持久性的存储。这对于实现像秘密浏览这种功能来说是很理想的。
- +backgroundSessionConfiguration:方法:该方法的独特之处在于,它会创建一个后台 session。后台session不同于普通的session,它甚至可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程(daemon)提供上下文。
- NSURLSessionTask
- -cancel方法:取消任务
- -resume方法:恢复任务
- -suspend方法:挂起任务
**GET请求的示例**
```Objective-C
NSURL *URL = [NSURL URLWithString:@"http://example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// ...
}];
[task resume];
上传的示例
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSData *data = ...;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// ...
}
];
[uploadTask resume];
下载的示例
NSURL *URL = [NSURL URLWithString:@"http://example.com/file.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
NSURL *newFileLocation = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];
[[NSFileManager defaultManager] copyItemAtURL:location toURL:newFileLocation error:nil];
}
];
[downloadTask resume];
NSURLSessionConfiguration拥有20项配置属性。熟练掌握这些配置属性的用处,可以让应用程序充分地利用其网络环境。
- HTTPAdditionalHeaders属性:指定了一组默认的可以设置请求头。
NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", user, password];
NSData * userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
NSString *authString = [NSString stringWithFormat:@"Basic %@", base64EncodedCredential];
NSString *userAgentString = @"AppName/com.example.app (iPhone 5s;iOS 7.0.2; Scale/2.0)";
configuration.HTTPAdditionalHeaders = @{
@"Accept": @"application/json",
@"Accept-Language": @"en",
@"Authorization": authString,
@"User-Agent": userAgentString
};
- allowsCellularAccess属性和discretionary属性:被用于节省通过蜂窝网络连接的带宽。对于后台传输的情况,推荐大家使用discretionary这个属性,而不是allowsCellularAccess,因为前者会把WIFI和电源的可用性考虑在内
- timeoutIntervalForRequest属性和timeoutIntervalForResource属性分别指定了对于请求和资源的超时间隔。许多开发人员试图使用timeoutInterval去限制发送请求的总时间,但其实它真正的含义是:分组(packet)之间的时间。实际上我们应该使用timeoutIntervalForResource来规定整体超时的总时间,但应该只将其用于后台传输,而不是用户实际上可能想要去等待的任何东西
- HTTPCookieStorage属性存储了session所使用的cookie。可以使用NSHTTPCookieShorage类的sharedHTTPCookieStorage类方法获得这个单例对象
- HTTPCookieAcceptPolicy决定了什么情况下session应该接受从服务器发出的cookie
- HTTPShouldSetCookies指定了请求是否应该包含cookie,也就是刚才我们提到的 HTTPCookieStorage属性的值,如果在请求中包含cookie,可以实现用户跟踪的功能
- URLCredentialStorage存储了session所使用的证书。默认情况下会使用 NSURLCredentialStorage的sharedCredentialStorage类方法获得这个单例对象
- TLSMaximumSupportedProtocol和TLSMinimumSupportedProtocol确定session是否支持 SSL协议。
- URLCache是session使用的缓存。默认情况下会使用NSURLCache的sharedURLCache类方法会提供这个单例对象
- requestCachePolicy指定了一个请求的缓存响应应该在什么时候返回。这相当于NSURLRequest的cachePolicy方法。
和NSURLSession相关的有四个协议,分别是:NSURLSessionDelegate / NSURLSessionTaskDelegate / NSURLSessionDataDelegate / NSURLSessionDownloadDelegate
理解KVC和KVO##
KVC全称是Key Value Coding(键值对编程),其本质上是对NSObject类的扩展(NSObject类的Category),它提供一种机制来间接访问对象的属性。KVO全称Key Value Observing(键值对观察),是建立KVC之上的模型对象的观察者模式的实现。
KVO:Key-Value Observing(KVO)建立在KVC之上,它能够观察一个对象指定属性值的变化,在需要的时候产生对应的通知。
- valueForKey:方法用于以字符串调用对象的get属性方法,或者读取成员变量的值;与之相对的是setValue:forKey:,它用于以字符串调用对象的set属性方法,或者修改成员变量的值。
- 对于基本数据类型,KVC方法会对基本数据类型进行封装,基本数据类型封装为NSNumber,其他结构体类型封装为NSValue
- 在使用KVC时,如果找不到字符串对应的属性和成员变量时会调用valueForUndefinedKey:或者setValue:forUndefinedKey:这两个方法,默认情况下会抛出异常
- 在默认情况下KVC方法能够直接访问类的私有成员变量,如果我们不想这样,可以重写accessInstanceVariablesDirectly方法,并令其返回NO(默认是返回YES)
- KVC方法定义在NSKeyValueCoding类别中,该类别附加于NSObject类上,所以所有对象都具有这些方法
- 在一些特殊的类的对象上调用KVC方法会有特别的效果。对于数组NSArray、集合NSSet,调用valueForKey:会对每个数组和集合成员调用valueForKey:,并返回新的数组或者集合
- 在KVC中还有一种常用技术,称为键值链(Key Path)。键值链是用点将若干键相连的字符串,例如“manufacturer.product.name”。通过在对象上调用valueForKeyPath:或者setValue:forKeyPath:,我们就可以在一条语句中级联调用指定的属性。
💃🏻KVO##
// 当观察到被观察对象属性变化时要执行的回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))]) {
// NSLog(@"%@", change);
double currentProgress = [change[@"new"] doubleValue];
hud.progress = (float) currentProgress;
if (currentProgress >= 1.0) {
// 移除观察者(避免内存泄露的问题)
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
}
}```
##💃🏻YYModel##
@property (nonatomic, copy) NSArray *eduinfo;
@property (nonatomic, copy) NSArray *workinfo;
@property (nonatomic, readonly) NSString *fullBirthday;
// 设置容器类型的属性中的对象类型 辅助YYModel进行模型和JSON转换
+(NSDictionary *) modelContainerPropertyGenericClass {
// value should be Class or Class name.
return @{
@"eduinfo" : [CDEduInfo class],
@"workinfo" : [CDWorkInfo class]
};
}
- (NSString *) fullBirthday {
return [NSString stringWithFormat:@"%@年%@月%@日", _birthyear, _birthmonth, _birthday];
}
- (NSDictionary *)modelCustomPropertyMapper {
return @{
@"albumId" : @"id",
@"pictureCount" : @"pics",
@"albumName" : @"albumname"
};
}```
AFNetworking
AFNetworking是一个能够快速使用的iOS和MacOS X下的网络框架,它是构建在Foundation URL Loading System之上的,封装了网络的抽象层,可以方便的使用,是一个模块化架构并拥有丰富API的框架。
HTTP请求和操作
// GET请求
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"http://example.com/resources.json" parameters: nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
// 带有表单参数的POST请求
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
[manager POST:@"http://example.com/resources.json" parameters: parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
// 上传表单和文件
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
[manager POST:@"http://example.com/resources.json" parameters: parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL: filePath name:@"image" error: nil];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"Success: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
Session管理
// 创建一个下载任务
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL: URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest: request progress: nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory: NSDocumentDirectory inDomain: NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
// 创建一个上传任务
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Success: %@ %@", response, responseObject);
}
}];
[uploadTask resume];
// 创建一个带进度的上传任务
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSProgress *progress = nil;
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
// 创建请求对象
NSString *URLString = @"http://example.com";
NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]};
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil];
// 网络可达性检测
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status));
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
// 基于HTTP请求操作管理器的网络可达性检测
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/"];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL: baseURL];
NSOperationQueue *operationQueue = manager.operationQueue;
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
[operationQueue setSuspended: NO];
break;
case AFNetworkReachabilityStatusNotReachable:
default:
[operationQueue setSuspended:YES];
break;
}
}];
[manager.reachabilityManager startMonitoring];
// 批量任务处理
NSMutableArray *mutableOperations = [NSMutableArray array];
for (NSURL *fileURL in filesToUpload) {
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[mutableOperations addObject:operation];
}
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
NSLog(@"All operations in batch complete");
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
下拉刷新和上拉刷新(触底刷新)##
- UIRefreshControl和触底刷新的原理
self.tableView.delegate = self; self.ref = [[UIRefreshControl alloc] init]; self.ref.tintColor = [UIColor orangeColor]; [self.ref addTarget:self action:@selector(doRefresh) forControlEvents:UIControlEventValueChanged]; [self.tableView addSubview:self.ref];
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if(scrollView.contentOffset.y + scrollView.frame.size.height > scrollView.contentSize.height + 50) { } }
- 第三方库:MJRefresh
💃🏻iOS8 以前的联网方式
// NSData和NSString通过URL获取网络资源属于同步请求
// 这就意味着在获取资源的过程中代码会阻塞直到得到资源
// 因此可能会导致界面出现假死状态 所以如果可以的话
// 我们应当尽可能使用异步请求(非阻塞式请求)来获取网络资源
// iOS 8 以前的联网方式
NSString *username = _uidField.text;
NSString *password = _pwdField.text;
// 提示: 如果用户名有非ASCII字符需要转换成对应的百分号编码
NSString *urlStr = [NSString stringWithFormat:@"http://10.0.8.8/sns/my/login.php?username=%@&password=%@", [username stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], password];
// 1. 创建一个统一资源定位符对象
NSURL *url = [NSURL URLWithString:urlStr];
// 2. 创建一个请求对象
// 第一个参数: 统一资源定位符
// 第二个参数: 缓存策略
// 第三个参数: 请求超时时间
NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5];
// 3. 通过连接对象发送请求
// 同步请求 - 阻塞式请求(代码会在此阻塞直到服务器完成响应或超时)
// 异步请求 - 非阻塞式请求(代码会往下执行, 当收到服务器数据时通过Block回调处理服务器返回的数据)
NSURLResponse *resp = nil;
NSError *err = nil;
// NSURLSession是NSURLConnection的继任者
// AFNetworking第三方库
NSData *data = [NSURLConnection sendSynchronousRequest:req returningResponse:&resp error:&err];
// 4. 解析数据
if (!err) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:1 error:nil];
[CDUtility showAlertWithMessage:dict[@"message"] onViewController:self];
if ([dict[@"code"] isEqualToString:@"login_success"]) {
// 通过单例对象保存用户的uid
[CDSNSContext sharedSNSContext].uid = [dict[@"uid"] integerValue];
CDUserInfoViewController *userInfoVC = [[CDUserInfoViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:userInfoVC];
self.view.window.rootViewController = nav;
} }
else {
NSLog(@"%@", err);
}
💃🏻NSURLConnection##
NSString *urlStr = [NSString stringWithFormat:@"http://10.0.8.8/sns/my/register.php?username=%@&password=%@&email=%@", [username stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], password, email];
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5];
// 发送异步请求
// 第一个参数: 请求对象
// 第二个参数: 操作队列(异步请求操作放在哪个队列中执行)
// 第三个参数: Block回调(处理服务器返回的数据)
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:1 error:nil];
if ([dict[@"code"] isEqualToString:@"registered"]) {
// 反向传值
if (_handler) {
_handler(username, password);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
else {
[CDUtility showAlertWithMessage:dict[@"message"] onViewController:self];
}
}
else {
NSLog(@"%@", connectionError);
}
sender.enabled = YES;
}];
💃🏻NSURLSession联网##
// NSURLSessionConfiguration对象可以保存公共设置
// 设置一次之后通过该对象创建的NSURLSession都使用相同的设置
// 创建一个默认的配置
// config = [NSURLSessionConfiguration defaultSessionConfiguration];
// config.HTTPAdditionalHeaders = @{
// @"apikey" : @"ae774eed68e97890749971a8a5a8e3e4"
// };
// config.timeoutIntervalForRequest = 5;
// config.discretionary = YES;
// 创建一个无痕浏览的配置
// config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
// 创建一个后台运行的配置
// config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"ABC"];
- (void) addAlbumButtonClicked:(UIBarButtonItem *) sender {
UIAlertController *ac = [UIAlertController alertControllerWithTitle:@"创建相册" message:@"请输入相册的名称" preferredStyle:UIAlertControllerStyleAlert];
// 给警告控制器中添加文本框
[ac addTextFieldWithConfigurationHandler:^(UITextField *textField) {
// 此处可以对文本框进行定制
textField.clearButtonMode = UITextFieldViewModeAlways;
textField.delegate = self;
textField.returnKeyType = UIReturnKeyDone;
}];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// 点击确定按钮就创建相册
UITextField *textField = [ac.textFields firstObject];
NSString *name = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (name.length > 0) {
[self createAlbum:name];
}
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[ac addAction:okAction];
[ac addAction:cancelAction];
[self presentViewController:ac animated:YES completion:nil];
}```
##💃🏻NSURLSession联网##
-
(void) createAlbum:(NSString *) name {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://10.0.8.8/sns/my/create_album.php?albumname=%@&privacy=0", [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];// 通过NSURLSessionConfiguration对象创建NSURLSession对象
NSURLSession *session = [NSURLSession sessionWithConfiguration:[CDSNSContext sharedSNSContext].configure delegate:self delegateQueue:nil];
// 通过指定的NSURL对象创建一个获取数据的任务
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:1 error:nil];
if ([dict[@"code"] isEqualToString:@"do_success"]) {
CDAlbum *model = [[CDAlbum alloc] init];
model.albumId = [dict[@"albumid"] integerValue];
model.albumName = name;
model.pictureCount = 0;
// 将模型对象放在装模型的数组中
[dataArray insertObject:model atIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程在表格视图中插入新行
[myTableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
});
}
else {
NSLog(@"然并卵!!!");
}
}
else {
NSLog(@"%@", error);
}}];
// 执行任务
[task resume];
}```
💃🏻NSURLSession联网获取数据、NSOperation##
- (void) loadDataModel {
if (!dataArray) {
dataArray = [NSMutableArray array];
}
NSURL *url = [NSURL URLWithString:@"http://10.0.8.8/sns/my/album_list.php"];
NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:5];
// 创建默认设置的网络请求会话
NSURLSession *session = [NSURLSession sharedSession];
// 通过会话创建一个获取数据的任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:1 error:nil];
for (NSDictionary *albumDict in dict[@"albums"]) {
// 通过MJExtension将字典转成模型对象
CDAlbum *model = [CDAlbum yy_modelWithDictionary:albumDict];
[dataArray addObject:model];
}
// 模型对象准备就绪后就可以刷新表格视图了
// 苹果官方规定: 刷新界面的代码要在主线程中执行
// 否则界面可能无法刷新 因此下面的代码要回到主线程执行
// 写法一: GCD
// dispatch_async(dispatch_get_main_queue(), ^{
// [myTableView reloadData];
// });
// 写法二: NSOperation和NSOperationQueue
// 创建一个操作对象封装要执行的代码
NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[myTableView reloadData];
}];
// 将该操作添加到主队列(主队列中的操作在主线程中执行)中
[[NSOperationQueue mainQueue] addOperation:op];
}
else {
NSLog(@"%@", error);
}
}];
// 任务恢复
[task resume];
// 任务挂起
// [task suspend];
// 任务取消
// [task cancel];
}```
##💃🏻AFNetworking上传照片##
// 上传照片
-
(void) uploadPhoto:(UIImage *) photoImage {
NSDictionary *params = @{ @"albumid": @(_albumId) };
// 通过AFHTTPRequestSerializer对象创建请求对象
// AFHTTPRequestSerializer对象创建请求的方法有五个参数:
// 第一个参数: 设置请求方法(如果上传表单数据中如果有附件(二进制数据)必须使用POST请求)
// 第二个参数: 统一资源定位符
// 第三个参数: 提交给服务器的请求参数(表单中的参数)
// 第四个参数: 提交给服务器的二进制数据(表单中的附件)
// 第五个参数: NSError对象指针的指针
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://10.0.8.8/sns/my/upload_photo.php" parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// 通过UUID生成一个全局唯一的文件名
NSString *filename = [NSString stringWithFormat:@"%@.png", [NSUUID UUID].UUIDString];
// 将UIImage对象转成NSData对象(二进制数据)
NSData *data = UIImagePNGRepresentation(photoImage);
// 第一个参数: 上传的二进制数据
// 第二个参数: 上传文件对应的参数名(通过查API手册获得)
// 第三个参数: 上传文件的文件名(这个名字通常没用, 因为服务器通常会用自己的命名规则给上传的文件起名字来避免名字冲突)
// 第四个参数: MIME类型(告知服务器上传的文件的类型)
[formData appendPartWithFileData:data name:@"attach" fileName:filename mimeType:@"image/png"];
} error:nil
];// 通过会话配置对象创建AFNetworking的会话管理器对象
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[CDSNSContext sharedSNSContext].configure];
AFHTTPResponseSerializer *serializer = manager.responseSerializer;
serializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", nil];
// 表示完成进度的对象
NSProgress *progress = nil;
// 创建会话任务(获取数据任务、下载任务、上传任务)
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSLog(@"%@", responseObject);
if (!error) {
[CDUtility showAlertWithMessage:responseObject[@"message"] onViewController:self];
hud.labelText = responseObject[@"message"];
[hud hide:YES afterDelay:2];
}
else {
NSLog(@"%@", error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
}];
// 在状态栏显示网络活动指示器(旋转的小菊花)
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
hud.labelText = @"正在上传";
hud.opacity = 0.5;
[uploadTask resume];// KVO - Key Value Observing
// GoF设计模式中观察者模式的应用
// 给NSProgress对象添加一个观察者观察它指定属性的变化
[progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:nil];
}```
💃🏻AFNetworking创建请求##
- (void) loadDataModel {
if (!dataArray) {
dataArray = [NSMutableArray array];
}
// 创建会话管理器对象(AFNetworking提供的专门用于管理NSURLSession的对象)
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[CDSNSContext sharedSNSContext].configure];
// 设置AFNetworking支持的响应内容的MIME类型
// text/html - 超文本
// text/plain - 纯文本
// application/json - JSON
// 说明: 有很多后台写得很烂的服务器(包括千锋SNS)在返回
// JSON数据的时候没有指定Content-Type响应头(MIME类型)是
// application/json类型 这样的话AFNetworking会报错
// 因为AFNetworking默认只支持application/json类型
AFHTTPResponseSerializer *serializer = manager.responseSerializer;
serializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html", @"application/json", @"text/plain", nil];
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://10.0.8.8/sns/my/photo_list.php?uid=%ld&id=%ld", [CDSNSContext sharedSNSContext].uid, _albumId]];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (!error) {
for (NSDictionary *photoDict in responseObject[@"photos"]) {
CDPhoto *model = [CDPhoto yy_modelWithDictionary:photoDict];
[dataArray addObject:model];
}
dispatch_async(dispatch_get_main_queue(), ^{
[myCollView reloadData];
});
}
else {
NSLog(@"Error: %@", error);
}
}];
[dataTask resume];
}```
##💃🏻AFNetworkReachabilityManager##
-
(void)viewDidLoad {
[super viewDidLoad];AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
hud.progress = 0.5;
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
hud.labelText = @"没有网络连接";
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
hud.labelText = @"使用移动蜂窝网络";
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
hud.labelText = @"使用WiFi网络";
default:
break;
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}];
[manager startMonitoring];
}```
💃🏻MJRefresh##
MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[self loadDataModel];
[myTableView.header endRefreshing];
}];
[header setTitle:@"放开那个师太!!!" forState:2];
[header setTitle:@"师太跟和尚跑了!" forState:3];
myTableView.header = header;
MJRefreshBackNormalFooter *footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
[self loadMoreDataModel];
[myTableView.footer endRefreshing];
}];
[footer setTitle:@"需要爷帮你加载更多吗?" forState:2];
myTableView.footer = footer;
💃🏻UIStoryboardSegue##
// 定制Storyboard上segue(连接)的回调方法
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// 通过segue对象获得目标视图控制器
CDRegViewController *regVC = segue.destinationViewController;
// 绑定Block属性实现反向传值
regVC.handler = ^(NSString *username, NSString *password) {
_uidField.text = username;
_pwdField.text = password;
};
}```
----------------------------------------------------