iOS网络编程(7) 第三方开源库—–>AFNetworking

最后更新于:2022-04-01 22:59:21

AFNetworking是一个为 iOS 和 Mac OSX 制作的令人愉快的网络库,它建立在URL 装载系统框架的顶层,内置在Cocoa里,扩展了强有力的高级网络抽象。它的模块架构被良好的设计,拥有丰富的功能,因此,使用起来,必定赏心悦目。        @原文链接https://github.com/AFNetworking/AFNetworking,我在此基础上了点配置修改        @介绍   1.支持HTTP请求和基于REST的网络服务(包括GET、POST、 PUT、DELETE等)   2.支持ARC   3.要求iOS 5.0及以上版本   4.UIKit扩展        @配置        1.下载[AFNetworking](http://afnetworking.com/),将2个文件夹:AFNetworking和UIKit+AFNetworking拖入工程        2.导入以下库文件:CFNetwork、Security、SystemConfiguration、MobileCoreServices        3.如果你以前用的是1.0版本,那么[AFNetworking 2.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-2.0-Migration-Guide)能帮助你        4.如果你是用CocoaPods配置的,那么            platform:ios,'7.0'            pod"AFNetworking","~>2.0"            @使用            1.HTTP请求操作            AFHTTPRequestOperationManager封装的共同模式与web应用程序通过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); }]; ~~~            POST请求(多表) ~~~ 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 formData) { [formData appendPartWithFileURL:filePath name:@"image" error:nil]; } success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"Success: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; ~~~              2.AFURLSessionManager(NSURLSession详细见[网络编程(6)](http://blog.csdn.net/hmt20130412/article/details/29412349))              创建和管理制定的NSURLSession对象NSURLSessionConfiguration对象必须实现, , , 协议               创建一个下载任务 ~~~ 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 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]; ~~~               创建一个数据流Data任务 ~~~ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { if (error) { NSLog(@"Error: %@", error); } else { NSLog(@"%@ %@", response, responseObject); } }]; [dataTask resume]; ~~~                 3.网络监测(一般会用另一个网络监测类,Reachability,还有JSON解析方法,反正我也一般不用,自行脑补)                 AFNetworkReachabilityManager监控网络领域的可达性,WWAN地址和WiFi接口.                当前网络状态 ~~~ [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); }]; ~~~               HTTP Manager 可达性 ~~~ 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; } }]; ~~~                   4.AFHTTPRequestOperation                   AFHTTPRequestOperation是使用HTTP或HTTPS协议的AFURLConnectionOperation的子类。 它封装的获取后的HTTP状态和类型将决定请求的成功与否。虽然AFHTTPRequestOperationManager通常是最好的去请求的方式,但是AFHTTPRequestOpersion也能够单独使用。                  GET请求 ~~~ NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request]; op.responseSerializer = [AFJSONResponseSerializer serializer]; [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"JSON: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; [[NSOperationQueue mainQueue] addOperation:op]; ~~~                批量多请求 ~~~ NSMutableArray *mutableOperations = [NSMutableArray array]; for (NSURL *fileURL in filesToUpload) { NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id 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]; ~~~                   @其他资料                  1.[官网](http://afnetworking.com/)                  2.[英文文档](http://cocoadocs.org/docsets/AFNetworking/2.0.3/)
';

iOS网络编程(六) NSURLSession详解

最后更新于:2022-04-01 22:59:18

昨夜浏览Demo的时候,看到别人请求网络数据用的是NSURLSession,当时就在想这里什么,怎么没有用过,引起了我的好奇心,遂去百度-谷歌-官方文档一一查看,有了一定的了解,原来NSURLSession是iOS7中新的网络接口,它与咱们熟悉的NSURLConnection是并列的。       查找资料,写了一个小Demo,大家可以看看,有什么不足的地方,可以留言帮我指出来. ~~~ // // HMTRootViewController.m // // // Created by HMT on 14-6-7. // Copyright (c) 2014年 胡明涛. All rights reserved. // #import "HMTRootViewController.h" #import "HMTAppDelegate.h" @interface HMTRootViewController () @property (nonatomic,strong)UIImageView *imageView; @property (nonatomic,strong)UIProgressView *progressIndicator; @property (nonatomic,strong)NSURLSession *urlSession; // 普通会话 //@property (nonatomic,strong)NSURLSession *backgroundSession; // 后台会话 @property (nonatomic,strong)NSURLSessionDownloadTask *sessionDownloadTask; // 下载Task @property (nonatomic,strong)NSURLSessionDownloadTask *resumableTask; // 恢复下载Task @property (nonatomic,strong)NSURLSessionDownloadTask *backgroundTask; // 后台下载Task @property (nonatomic,strong)NSData *partialData; // 下载的局部数据 @end @implementation HMTRootViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad{ [super viewDidLoad]; self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 64, 320, 300)]; [self.view addSubview:_imageView]; self.progressIndicator = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; _progressIndicator.frame = CGRectMake(50, 500, 220, 50); [self.view addSubview:_progressIndicator]; UIButton *cancleButton = [UIButton buttonWithType:UIButtonTypeSystem]; cancleButton.frame = CGRectMake(120, 400, 40, 40); [cancleButton setTitle:@"取消" forState:UIControlStateNormal]; [cancleButton addTarget:self action:@selector(didClickCancleButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:cancleButton]; UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeSystem]; downloadButton.frame = CGRectMake(20, 400, 40, 40); [downloadButton setTitle:@"下载" forState:UIControlStateNormal]; [downloadButton addTarget:self action:@selector(didClickDownloadButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:downloadButton]; UIButton *uploadButton = [UIButton buttonWithType:UIButtonTypeSystem]; uploadButton.frame = CGRectMake(70, 400, 40, 40); [uploadButton setTitle:@"上传" forState:UIControlStateNormal]; [uploadButton addTarget:self action:@selector(didClickUploadButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:uploadButton]; UIButton *resumableButton = [UIButton buttonWithType:UIButtonTypeSystem]; resumableButton.frame = CGRectMake(180, 400, 40, 40); [resumableButton setTitle:@"恢复" forState:UIControlStateNormal]; [resumableButton addTarget:self action:@selector(didClickResuableButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:resumableButton]; UIButton *backgroundLoadButton = [UIButton buttonWithType:UIButtonTypeSystem]; backgroundLoadButton.frame = CGRectMake(220, 400, 80, 40); [backgroundLoadButton setTitle:@"后台下载" forState:UIControlStateNormal]; [backgroundLoadButton addTarget:self action:@selector(didClickBackgroundButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:backgroundLoadButton]; #pragma mark - 如果我们需要利用NSURLSession进行数据传输我们需要: /** * 创建一个NSURLSessionConfiguration,用于创建NSSession时设置工作模式(3种) * (1)一般模式(default):工作模式类似于原来的NSURLConnection,可以使用缓存的Cache,Cookie,鉴权。 * (2)及时模式(ephemeral):不使用缓存的Cache,Cookie,鉴权。 * (3)后台模式(background):在后台完成上传下载,创建Configuration对象的时候需要给一个NSString的ID用于追踪完成工作的Session是哪一个 */ NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; /** * @网络设置:参考NSURLConnection中的设置项 * 两种创建方法(目前不太懂什么区别) * (1)就是根据刚才创建的Configuration创建一个Session,系统默认创建一个新的OperationQueue处理Session的消息 * (2)可以设定回调的delegate(注意这个回调delegate会被强引用),并且可以设定delegate在哪个OperationQueue回调,如果我们将其 * 设置为[NSOperationQueue mainQueue]就能在主线程进行回调非常的方便 */ //NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig]; self.urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; NSURL *url = [NSURL URLWithString:@"http://www.bizhiwa.com/uploads/allimg/2012-01/22021207-1-311536.jpg"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; /** * NSURLSessionUploadTask:上传用的Task,传完以后不会再下载返回结果; * NSURLSessionDownloadTask:下载用的Task; * NSURLSessionDataTask:可以上传内容,上传完成后再进行下载。 */ self.sessionDownloadTask = [self.urlSession downloadTaskWithRequest:request]; // 同NSURLConnection一样,有代理方法也就有block方法 // [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // }]; } #pragma mark 点击下载 - (void)didClickDownloadButtonAction:(UIButton *)button{ // 因为任务默认是挂起状态,需要恢复任务(执行任务) [_sessionDownloadTask resume]; } #pragma mark 点击上传 - (void)didClickUploadButtonAction:(UIButton *)button{ //判断imageView是否有内容 if (_imageView.image == nil) { NSLog(@"image view is empty"); return; } // 0. 上传之前在界面上添加指示符 UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; // 设置位置??? CGSize size = _imageView.bounds.size; indicator.center = CGPointMake(size.width / 2.0, size.height / 2.0); [self.imageView addSubview:indicator]; [indicator startAnimating]; // 1. URL NSURL *url = [NSURL URLWithString:@"http://www.bizhiwa.com/uploads/allimg/2012-01/22021207-1-311536.jpg"]; // 2. Request -> PUT,request的默认操作是GET NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0f]; request.HTTPMethod = @"PUT"; // *** 设置网络请求的身份验证! *** // 1> 授权字符串 NSString *authStr = @"admin:123456"; // 2> BASE64的编码,避免数据在网络上以明文传输 // iOS中,仅对NSData类型的数据提供了BASE64的编码支持 NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding]; NSString *encodeStr = [authData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; NSString *authValue = [NSString stringWithFormat:@"Basic %@", encodeStr]; [request setValue:authValue forHTTPHeaderField:@"Authorization"]; // 3. Session NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 4. UploadTask NSData *imageData = UIImageJPEGRepresentation(_imageView.image, 0.75); // 应用block的请求方式 NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // 上传完成后,data参数转换成string就是服务器返回的内容 NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"OK -> %@", str); [NSThread sleepForTimeInterval:5.0f]; dispatch_async(dispatch_get_main_queue(), ^{ [indicator stopAnimating]; [indicator removeFromSuperview]; }); }]; // 因为任务默认是挂起状态,需要恢复任务(执行任务) [uploadTask resume]; } #pragma mark 点击取消 // NSURLConnection一旦发送是没法取消的。但是,我们可以很容易的取消掉一个NSURLSessionTask任务 - (void)didClickCancleButtonAction:(UIButton *)button{ /** * 当取消后,会回调这个URLSession:task:didCompleteWithError:代理方法,通知你去及时更新UI。当取消一个任务后,也 * 十分可能会再一次回调这个代理方法URLSession:downloadTask:didWriteData:BytesWritten:totalBytesExpectedToWrite: * 当然,didComplete 方法肯定是最后一个回调的。 */ // if (_sessionDownloadTask) { // // // 取消下载请求 // [_sessionDownloadTask cancel]; // _sessionDownloadTask = nil; // } if (!self.sessionDownloadTask) { // 停止下载任务,把待恢复的数据保存到一个变量中,方便后面恢复下载使用 [self.sessionDownloadTask cancelByProducingResumeData:^(NSData *resumeData) { self.partialData = resumeData; self.sessionDownloadTask = nil; }]; } } #pragma mark 恢复下载(断点续传) - (void)didClickResuableButtonAction:(UIButton *)button{ if (self.partialData) { self.sessionDownloadTask = [self.urlSession downloadTaskWithResumeData:self.partialData]; self.partialData = nil; }else{ NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pic4.duowan.com/wow/1002/130782267821/130782458426.jpg"]]; self.resumableTask = [self.urlSession downloadTaskWithRequest:request]; } [self.sessionDownloadTask resume]; } #pragma mark 后台下载模式 - (void)didClickBackgroundButtonAction:(UIButton *)button{ NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V3.1.2.dmg"]]; self.backgroundTask = [[self backgroundSession] downloadTaskWithRequest:request]; [self.backgroundTask resume]; } #pragma mark - NSURLSessionDownloadTaskDelegate // 下载完成 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ /** *******->appDelegete里面的方法 typedef void(^MyBlock)(); @property (copy, nonatomic) MyBlock backgroundURLSessionCompletionHandler; // 后台请求结束时调用的方法 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ self.backgroundURLSessionCompletionHandler = completionHandler; } */ // 如果是后台NSURLSession,后台请求结束后会调用这个方法,通知你应该更新UI了 if (session == [self backgroundSession]) { self.backgroundTask = nil; HMTAppDelegate *appDelegate = (HMTAppDelegate *)[UIApplication sharedApplication].delegate; if (appDelegate.backgroundURLSessionCompletionHandler) { void(^handler)() = appDelegate.backgroundURLSessionCompletionHandler; appDelegate.backgroundURLSessionCompletionHandler = nil; handler(); } } // 这里的缓存处理做的不好,大家按自己的方法处理就行,还有图片的存储以它本身的URL路径为准,这样是不会有重复的 NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *cachesURLPath = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject]; // 根据URL获取到下载的文件名,拼接成沙盒的存储路径(location是下载的临时文件目录,在tmp文件夹里面) NSURL *destinationPath = [cachesURLPath URLByAppendingPathComponent:[location lastPathComponent]]; NSError *error = nil; BOOL success = [fileManager moveItemAtURL:location toURL:destinationPath error:&error]; [fileManager removeItemAtURL:location error:NULL]; // location是下载的临时文件目录,将文件从临时文件夹复制到沙盒 // BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error]; if (success) { dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithContentsOfFile:[destinationPath path]]; self.imageView.image = image; // UIImageView会自动裁剪图片适应它的frame,下面这个属性就是展示原图 self.imageView.contentMode = UIViewContentModeScaleAspectFill; }); } } // 不管任务是否成功,在完成后都会回调这个代理方法 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ // 如果error是nil,则证明下载是成功的,否则就要通过它来查询失败的原因。如果下载了一部分,这个error会包含一个NSData对象,如果后面要恢复任务可以用到 if (error == nil) { dispatch_async(dispatch_get_main_queue(), ^{ self.progressIndicator.hidden = YES; }); } } // 传输进度 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ double currentValue = totalBytesWritten / (double)totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%f",currentValue); self.progressIndicator.hidden = NO; self.progressIndicator.progress = currentValue; }); } // 未知 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ } #pragma mark - NSURLSession另一个重要的特性:即使当应用不在前台时,你也可以继续传输任务。当然,我们的会话模式也要为后台模式 - (NSURLSession *)backgroundSession{ // 通过给的后台token,我们只能创建一个后台会话,所以这里使用dispatch once block static NSURLSession *backgroundSession = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.shinobicontrols.BackgroundDownload.BackgroundSession"]; backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; }); return backgroundSession; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ @end ~~~ @不错的NSURLSession文章 [iOS 7系列译文:忘记NSURLConnection,拥抱NSURLSession吧!](http://jishu.zol.com.cn/201091.html)
';

iOS网络编程(五) 异步加载及缓存图片—–EGO

最后更新于:2022-04-01 22:59:16

@EGOImageView是一个很常用的异步加载及缓存网络图片的第三方类,相比SDWebImage,个人感觉EGOImageView更简单(PS:EGOImageView不支持ARC,SDWebImage3.0支持ARC) **@EGOImageView的导入** 1.下载:https://github.com/enormego/EGOImageLoading(下载后运行demo程序XCode会提示找不到EGOCache.h头文件,可以在这个地方下载https://github.com/enormego/EGOCache) 2.将EGOCache、EGOImageButton、EGOImageView、EGOImageLoader全部添加到工程下(拷贝) 3.EGOImageView是不支持ARC的,在ARC的工程中要注意,可参考[MRC工程配置ARC](http://blog.csdn.net/hmt20130412/article/details/24036871) **@代码示例:** ~~~ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _button = [[HMTBlockButton alloc]initWithFrame:CGRectMake(110, 510, 100, 50)]; _button.backgroundColor = [UIColor redColor]; [_button setTitle:@"加载" forState:UIControlStateNormal]; [self.view addSubview:_button]; [_button release]; // 将系统给的Button封装成了Block形式 _button.blockButton = ^(HMTBlockButton * button){ /** * placeholder.png 是图片还未加载完成时显示的图片,在APP中经常能看到图片未加载完成显示是该APP主题的 * 图片,当加载过程完成之后就会显示url对应的图片。 */ _imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:@"placeholder.png"]]; _imageView.imageURL = [NSURL URLWithString:@"http://i0.sinaimg.cn/ent/s/m/2011-08-19/U3904P28T3D3391507F329DT20110819143720.jpg"]; _imageView.frame = CGRectMake(60, 30, 200, 400); [self.view addSubview:_imageView]; [_imageView release]; }; // 作用:清除缓存 [[EGOCache globalCache] clearCache]; // 获得已经下载了的图片对象 UIImage * image = [[EGOImageLoader sharedImageLoader]imageForURL:[NSURL URLWithString:@"http://i0.sinaimg.cn/ent/s/m/2011-08-19/U3904P28T3D3391507F329DT20110819143720.jpg"] shouldLoadWithObserver:nil]; } ~~~ **@注意事项:**          最近上网查资料看到,用EGOImageView的一个bug 当imageView的图片加载完成了,这时你想换一个图片的url并用EGOImageView加载这个图片时,需要重新设置EGOImageView的imageURL属性。**但是这里要特别注意的是这个方法必须在主线程中执行** ~~~ _button1 = [[HMTBlockButton alloc]initWithFrame:CGRectMake(160, 510, 100, 50)]; _button1.backgroundColor = [UIColor redColor]; [_button1 setTitle:@"换图" forState:UIControlStateNormal]; [self.view addSubview:_button1]; [_button1 release]; // 将系统给的Button封装成了Block形式 _button1.blockButton = ^(HMTBlockButton * button){ // 在主线程中进行 dispatch_async(dispatch_get_main_queue(), ^{ _imageView.imageURL = [NSURL URLWithString:@"http://www.sinaimg.cn/dy/slidenews/3_img/2012_10/28891_147237_598867.jpg"]; }); }; ~~~
';

iOS网络编程(四) 异步加载及缓存图片—–自定义类

最后更新于:2022-04-01 22:59:14

@通常,我们常常习惯于用第三方的库,来实现很多功能需求,方便简洁快速,但是,为了进一步的提升自己,我们可以尝试去了解它内部的实现机制,所有的功能实现,万变不离其宗 @下面代码示例,都是很基础的知识,来实现这一功能 ~~~ #import @interface ImageDownloader : NSObject @property (nonatomic,copy) NSString * imageUrl; //开始下载图像 - (void)startDownloadImage:(NSString *)imageUrl; //从本地加载图像 - (UIImage *)loadLocalImage:(NSString *)imageUrl; @end #import "ImageDownloader.h" @implementation ImageDownloader - (void)dealloc { self.imageUrl = nil; Block_release(_completionHandler); [super dealloc]; } #pragma mark - 异步加载 - (void)startDownloadImage:(NSString *)imageUrl { self.imageUrl = imageUrl; // 先判断本地沙盒是否已经存在图像,存在直接获取,不存在再下载,下载后保存 // 存在沙盒的Caches的子文件夹DownloadImages中 UIImage * image = [self loadLocalImage:imageUrl]; if (image == nil) { // 沙盒中没有,下载 // 异步下载,分配在程序进程缺省产生的并发队列 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 多线程中下载图像--->方便简洁写法 NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; // 缓存图片 [imageData writeToFile:[self imageFilePath:imageUrl] atomically:YES]; // 回到主线程完成UI设置 dispatch_async(dispatch_get_main_queue(), ^{ UIImage * image = [UIImage imageWithData:imageData]; /** * ............ 进行UI设置 * ............ imageView.image = image; * 也可以利用blcok,将image对象传到别处去 */ }); }); } } #pragma mark - 加载本地图像 - (UIImage *)loadLocalImage:(NSString *)imageUrl { self.imageUrl = imageUrl; // 获取图像路径 NSString * filePath = [self imageFilePath:self.imageUrl]; UIImage * image = [UIImage imageWithContentsOfFile:filePath]; if (image != nil) { return image; } return nil; } #pragma mark - 获取图像路径 - (NSString *)imageFilePath:(NSString *)imageUrl { // 获取caches文件夹路径 NSString * cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSLog(@"caches = %@",cachesPath); // 创建DownloadImages文件夹 NSString * downloadImagesPath = [cachesPath stringByAppendingPathComponent:@"DownloadImages"]; NSFileManager * fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:downloadImagesPath]) { [fileManager createDirectoryAtPath:downloadImagesPath withIntermediateDirectories:YES attributes:nil error:nil]; } #pragma mark 拼接图像文件在沙盒中的路径,因为图像URL有"/",要在存入前替换掉,随意用"_"代替 NSString * imageName = [imageUrl stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; NSString * imageFilePath = [downloadImagesPath stringByAppendingPathComponent:imageName]; return imageFilePath; } @end ~~~
';

iOS网络编程(三) 异步加载及缓存图片—->SDWebImage

最后更新于:2022-04-01 22:59:12

**@SDWebImage提供一个UIImageView的类别以支持加载来自网络的远程图片。具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征.** **@SDWebImage的导入** 1.https://github.com/rs/SDWebImage 下载SDWebImage开源包 2.将类包拖入工程,再导入MapKit.framework、ImageIO.framework两个框架 3.SDWebImage是支持ARC的,在MRC的工程中要注意,可参考[MRC工程配置ARC](http://blog.csdn.net/hmt20130412/article/details/24036871) 4.注意:SDWebImage 3.0不向后兼容2.0并且最低需要iOS 5.0 的版本,而且提供的方法多数是Blcok形式 **@目前来说,主要用到了"UIImageView+WebCache.h",给出一个代码示例:**         它是UIImageView的一个类目,在要使用它的类中加入#import "UIImageView+WebCache.h",调用                setImageWithURL:placeholderImage:方法。从异步下载到缓存管理,一切都会为你处理。 ~~~ - (void)createImageView{ self.imageArray = [NSMutableArray array]; self.urlArray = @[@"http://c.hiphotos.baidu.com/image/w%3D2048/sign=396e9d640b23dd542173a068e531b2de/cc11728b4710b9123a8117fec1fdfc039245226a.jpg", @"http://e.hiphotos.baidu.com/image/w%3D2048/sign=c9c32d60f1deb48ffb69a6dec4273b29/960a304e251f95cae5f125b7cb177f3e670952ae.jpg", @"http://f.hiphotos.baidu.com/image/w%3D2048/sign=0e0fe1d417ce36d3a20484300ecb3b87/3801213fb80e7bec015d1eef2d2eb9389b506b3c.jpg", @"http://a.hiphotos.baidu.com/image/w%3D2048/sign=6e8e7ce5b11c8701d6b6b5e613479f2f/b3fb43166d224f4a6059b1120bf790529922d1eb.jpg", @"http://f.hiphotos.baidu.com/image/w%3D2048/sign=e0608e290cf41bd5da53eff465e280cb/aec379310a55b31976baeb7741a98226cffc1774.jpg", @"http://g.hiphotos.baidu.com/image/w%3D2048/sign=4b5f112a0cf41bd5da53eff465e280cb/aec379310a55b319dd85747441a98226cffc17b6.jpg", @"http://h.hiphotos.baidu.com/image/w%3D2048/sign=35229123708b4710ce2ffaccf7f6c2fd/c995d143ad4bd113fc73de3058afa40f4bfb0571.jpg", @"http://b.hiphotos.baidu.com/image/w%3D2048/sign=ad8b74e88fb1cb133e693b13e96c574e/f9dcd100baa1cd11eba86d27bb12c8fcc3ce2d9e.jpg", @"http://e.hiphotos.baidu.com/image/w%3D2048/sign=ac1303f0a5efce1bea2bcfca9b69f2de/838ba61ea8d3fd1f7dc8e23c324e251f94ca5ff6.jpg", ]; for (int i = 0; i < 3; i ++) { for (int j = 0; j < 3; j++) { UIImageView * imageView = [[UIImageView alloc]init]; // 15 10 10 15 imageView.frame = CGRectMake(15+100*j, 80+140*i, 90, 120); imageView.backgroundColor = [UIColor redColor]; [self.view addSubview:imageView]; [self.imageArray addObject:imageView]; [imageView release]; } } UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem]; button.frame = CGRectMake(100, 510, 80, 40); [button setTitle:@"下载" forState:UIControlStateNormal]; [button addTarget:self action:@selector(onClickLoadButton) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } - (void)onClickLoadButton{ //SDWebImageManager * manager = [SDWebImageManager sharedManager]; for (int i = 0; i < [_imageArray count]; i++) { UIImageView * image = [_imageArray objectAtIndex:i]; // 异步加载及缓存图片一步到位 [image setImageWithURL:[NSURL URLWithString:[_urlArray objectAtIndex:i]] placeholderImage:[UIImage imageNamed:@"placeholder.png"]]; } } ~~~        查看图片是否进了缓存: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358afcd18f.jpg) **@得到1个图片的url用SDWebImage缓存后的文件名** ~~~ NSLog(@"%s__%d__|%@",__FUNCTION__,__LINE__,[[SDImageCache sharedImageCache] cachedFileNameForKey:@"http://c.hiphotos.baidu.com/image/w%3D2048/sign=396e9d640b23dd542173a068e531b2de/cc11728b4710b9123a8117fec1fdfc039245226a.jpg"]); ~~~ **@其他**       SDWebImage是个比较大的类库,还有其他一些类的用法,自己不太了解,欢迎大家留言给出意见,一起学习,下面给出一些比较好的博客文章(转的)       http://blog.csdn.net/shenjx1225/article/details/10444449 Using blocks        使用blocks,你将被告知下载进度,完成时是成功还是失败: ~~~ // Here we use the new provided setImageWithURL: method to load the web image [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {... completion code here ...}]; ~~~        注意:如果请求被取消,那么block不会被调用(无论成功还是失败)。 ### Using SDWebImageManager       The SDWebImageManager is the class behind the UIImageView+WebCache category. It ties the asynchronous downloader with the image cache store. You can use this class directly to benefit from web image downloading with caching in another context than a UIView (ie: with Cocoa)       下面是如何使用SDWebImageManager的代码: ~~~ SDWebImageManager *manager = [SDWebImageManager sharedManager]; [manager downloadWithURL:imageURL options:0 progress:^(NSUInteger receivedSize, long long expectedSize) { // progression tracking code } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { if (image) { // do something with image } }]; ~~~ Using Asynchronous Image Downloader Independently       也能够独立地使用异步图像下载: ~~~ [SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL options:0 progress:^(NSUInteger receivedSize, long long expectedSize) { // progression tracking code } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { if (image && finished) { // do something with image } }]; ~~~ Using Asynchronous Image Caching Independently         也可以独立地使用基于异步的图像缓存存储。SDImageCache维护一个内存缓存和一个可选的磁盘缓存。磁盘高 速缓存的写操作是异步进行的,所以它不会给UI增加不必要的延迟 。                  为了方便,SDImageCache类提供了一个单例,但是如果你想创建单独的缓存命名空间你也可以创建新的实例。         你可以使用imageForKey:方法来查找缓存,如果返回为nil,说明当前图像不拥有缓存。因此你负责生成并缓存它。缓存键(cache key)是一个程序中图像缓存的唯一标识符,他通常是图像的url。 ~~~ SDImageCache *imageCache = [SDImageCache.alloc initWithNamespace:@"myNamespace"]; [imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) { // image is not nil if image was found }]; ~~~         默认情况下,如果一个图像不能在内存缓存中找到,SDImageCache将会查找高速缓存。你可以调用替代的方法imageFromMemoryCacheForKey:来预防这种情况的发生。         存储一个图像到缓存,你可以使用storeImage:forKey: 方法: ~~~ [[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey]; ~~~         默认情况下,图像将被存储在内存上的缓存以及磁盘上的缓存(异步)。 如果你想只在内存中缓存,使用替代方法storeImage:forKey:toDisk:,第三个参数为负数。 ### Using cache key filter         有时你也许不想使用图像URL作为缓存键,因为URL可能是动态的(i.e.: for access control purpose)。SDWebImageManager provides a way to set a cache key filter that takes the NSURL as input, and output a cache key NSString(这句不大理解)。         下面的示例在应用程序的委托中设置一个过滤器,在使用它的缓存键之前将从URL中删除任何查询字符串(The following example sets a filter in the application delegate that will remove any query-string from the URL before to use it as a cache key): ~~~ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { SDWebImageManager.sharedManager.cacheKeyFilter:^(NSURL *url) { url = [[[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path] autorelease]; return [url absoluteString]; }; // Your app init code... return YES; } ~~~ ### Using dynamic image size with UITableViewCell       UITableView通过第一个单元格设置的图像决定图像的尺寸。如果您的远程图像没有图像占位符的大小相同,您可能会遇到奇怪的变形缩放问题。下面的文章给出了一个方法来解决这个问题: [http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/](http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/) ### Handle image refresh(控制图像刷新)         默认情况下,SDWebImage确实非常积极的缓存。它忽略了所有类型的通过HTTP服务器返回的缓存控制头,并 且没有时间限制地缓存返回的图像。这意味着你的图像url是永远不会改变的、指向图像的静态url。如果指向的图片 发生了变化,那么url也会相应的跟着变化。         如果你不控制你的图像服务器,当它的内容更新时你不能改变它的url。Facebook头像就是这种情况的例子。在这种情况下,你可以使用SDWebImageRefreshCached的标志。这将稍微降低性能,但将会考虑到HTTP缓存控制头: ~~~ [imageView setImageWithURL:[NSURL URLWithString:@"https://graph.facebook.com/olivier.poitrey/picture"] placeholderImage:[UIImage imageNamed:@"avatar-placeholder.png"] options:SDWebImageRefreshCached]; ~~~ ### Add a progress indicator(添加进度指示) 查看这个类别: [https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage](https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage) **      @好像是SDWebImage2.0的流程介绍 http://blog.csdn.net/uxyheaven/article/details/7909373**
';

iOS网络编程(二) 自定义请求网络类—-推荐用于需要请求过程片段数据

最后更新于:2022-04-01 22:59:09

**@Block传值** **.h** ~~~ #import typedef void(^MyBlock)(NSData *); typedef void(^LengthBlock)(float); @interface HMTMyCustomNetRequest : NSObject @property (nonatomic,retain)NSMutableData * data; @property (nonatomic,retain)NSURLConnection * connection; @property (nonatomic,copy)MyBlock finishLoadBlock; @property (nonatomic,copy)LengthBlock lengthBlock; // GET请求方式 - (void)requestForGETWithUrl:(NSString *)urlString; // POST请求方式 - (void)requestForPOSTWithUrl:(NSString *)urlString postData:(NSData *)data; // 取消请求 - (void)cancelNSURLConnection; @end ~~~ **.m** ~~~ #import "HMTMyCustomNetRequest.h" @interface HMTMyCustomNetRequest (){ NSUInteger leng; } @end @implementation HMTMyCustomNetRequest - (void)dealloc{ RELEASE_SAFELY(_data); RELEASE_SAFELY(_connection); Block_release(_finishLoadBlock); Block_release(_lengthBlock); [super dealloc]; } - (void)requestForGETWithUrl:(NSString *)urlString{ NSURL * url = [NSURL URLWithString:urlString]; NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request setHTTPMethod:@"GET"]; /** * Special Considerations During the download the connection maintains a strong reference to the delegate. It releases that strong reference when the connection finishes loading, fails, or is canceled. */ self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; } - (void)requestForPOSTWithUrl:(NSString *)urlString postData:(NSData *)data{ NSURL * url = [NSURL URLWithString:urlString]; NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:data]; self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark - 取消协议异步方法调用(关键关键很关键,不做这一步经常出现崩溃的地方) - (void)cancelNSURLConnection{ /** * 如果数据在加载,但是因为其他一些原因,比如跳转页面做其他事,而不是正常的数据加载完成或者数据加载失败 * 一定要手动取消网络请求连接 * Special Considerations * During the download the connection maintains a strong reference to the delegate. It releases that strong reference when the-- * ---connection finishes loading, fails, or is canceled. * self.connection = [NSURLConnection connectionWithRequest:self delegate:self]; * */ [_connection cancel]; self.connection = nil; } #pragma mark - 服务器开始给客户端回传数据,这个方法只会执行一次 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ // 初始化接收数据的空data self.data = [NSMutableData data]; // 取得数据总长度 leng = response.expectedContentLength; } #pragma mark - 客户端持续接收数据,data是数据片段,整个数据分段返回,这个方法执行的次数取决于数据总长度[response expectedContentLength] - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [self.data appendData:data]; float progressLength = [self.data length] / (float)leng; if (_lengthBlock) { self.lengthBlock(progressLength); } //if (_finishLoadBlock) { //self.finishLoadBlock(_data); //} } #pragma mark - 数据完全下载成功,接收到完整数据 - (void)connectionDidFinishLoading:(NSURLConnection *)connection{ // 调用完整数据的block方法 if (_finishLoadBlock) { self.finishLoadBlock(_data); } } #pragma mark - 数据下载失败 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"请求网络出错:%@",error); } @end ~~~ **@delegate代理传值** **.h** ~~~ #import @class HMTNetWorkRequest; @protocol NetWorkRequestDelegate @optional //netWork请求成功 - (void)netWorkRequest:(HMTNetWorkRequest *)request didSuccessfulReceiveData:(NSData *)data; //netWork请求失败 - (void)netWorkRequest:(HMTNetWorkRequest *)request didFailed:(NSError *)error; //获取netWork的下载进度 - (void)netWorkRequest:(HMTNetWorkRequest *)request withProgress:(CGFloat)progress; @end @interface HMTNetWorkRequest : NSObject @property (nonatomic,assign) id delegate; @property (nonatomic,retain)NSURLConnection * connection; - (void)requestForGETWithUrl:(NSString *)urlString; - (void)requestForPOSTWithUrl:(NSString *)urlString postData:(NSData *)data; // 取消网络请求 - (void)cancelRequest; @end ~~~ **.m** ~~~ #import "HMTNetWorkRequest.h" @interface HMTNetWorkRequest (){ NSUInteger _totalLength; } @property (nonatomic,retain)NSMutableData * receiveData; @end @implementation HMTNetWorkRequest - (void)dealloc{ RELEASE_SAFELY(_connection); RELEASE_SAFELY(_receiveData); [super dealloc]; } #pragma mark - 不用的时候一定要取消 - (void)cancelRequest{ [_connection cancel]; self.connection = nil; self.delegate = nil; } - (void)requestForGETWithUrl:(NSString *)urlString{ NSURL * url = [NSURL URLWithString:urlString]; NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request setHTTPMethod:@"GET"]; /** * Special Considerations During the download the connection maintains a strong reference to the delegate. It releases that strong reference when the connection finishes loading, fails, or is canceled. */ [NSURLConnection connectionWithRequest:request delegate:self]; [request release]; } - (void)requestForPOSTWithUrl:(NSString *)urlString postData:(NSData *)data{ NSURL * url = [NSURL URLWithString:urlString]; NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:data]; [NSURLConnection connectionWithRequest:request delegate:self]; [request release]; } #pragma mark - 服务器开始给客户端回传数据,这个方法只会执行一次 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ // 服务器开始回传数据,客户端需要创建一个空的,可变的Data对象,用于存储每次获取的数据片段 self.receiveData = [NSMutableData data]; // 取得请求数据的长度 _totalLength = [response expectedContentLength]; } #pragma mark - 客户端持续接收数据,data是数据片段,整个数据分段返回,这个方法执行的次数取决于数据总长度[response expectedContentLength] - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [self.receiveData appendData:data]; CGFloat progress = [_receiveData length]/_totalLength; if ([_delegate respondsToSelector:@selector(netWorkRequest:withProgress:)]) { [_delegate netWorkRequest:self withProgress:progress]; } } #pragma mark - 数据完全下载成功,接收到完整数据 - (void)connectionDidFinishLoading:(NSURLConnection *)connection{ if ([_delegate respondsToSelector:@selector(netWorkRequest:didSuccessfulReceiveData:)]) { [_delegate netWorkRequest:self didSuccessfulReceiveData:_receiveData]; } self.receiveData = nil; } #pragma mark - 数据下载失败 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"didFailWithError"); } @end ~~~
';

iOS网络编程(一)NSURLConnection

最后更新于:2022-04-01 22:59:07

**1.访问网络的方式** **@同步请求:数据的请求在主线程来执行,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作,而网络数据加载需要一个时间过程,这样的话就会堵塞主线程.** **@异步请求:数据的请求过程在多线程执行** **@其他区别:同步请求无法取消,异步请求过程中可以取消;同步请求无法监听加载进度,异步可以监听** **2.主要请求方式:** **@GET请求:将参数直接写在访问路径上,操作简单,但是容易被外界看到,安全性不高,而且有长度限制,地址最多255字节** **@POST请求:将参数放到body里面.POST请求操作相对复杂,需要将参数和地址分开,不过安全性高,参数放在body里面不容易被捕获** **3.基本流程:** **@构造NSURL实例(将需要请求的数据的网址字符串转化成NSURL对象)** @生成*NSURLRequest*请求(不可变,一般用*NSMutableURLRequest*) **@通过NSURLConnection发送请求** **@通过返回*NSURLResponse*实例和*NSError*实例分析结果** **@接受返回数据** **4.NSURL(**NSURL实例包装了一个地址信息,可以是本地地址,也可以是远程地址) ~~~ // 初始化URL实例 NSURL * url = [NSURL URLWithString:@"http://www.baidu.com:8080/search?id=1"]; // http NSLog(@"Scheme:%@",[url scheme]); // www.baidu.com NSLog(@"Host:%@",[url host]); // 端口8080 NSLog(@"Port:%@",[url port]); // http://www.baidu.com:8080/search?id=1 NSLog(@"absoluteString:%@",[url absoluteString]); // /search NSLog(@"relativePath:%@",[url relativePath]); // /search NSLog(@"Path:%@",[url path]); // ("/",search ) NSLog(@"pathComponents:%@",[url pathComponents]); // id=1 NSLog(@"Query:%@",[url query]); ~~~ **5.NSURLRequest和NSMutableURLRequest** ~~~ @NSURLRequest 包装了网络请求的信息 NSURL * url = [NSURL URLWithString:urlString]; // 设置后不能改变 NSURLRequest * request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; @NSMutableURLRequest @便利构造器方法(方便,其他参数另行设置) NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; // 请求方式默认是GET,可以不设置 [request setHTTPMethod:@"GET”]; @实例方法 NSMutableURLRequest * request = [[NSMutableURLRequest alloc]init]; // 设置超时时间 [request setTimeoutInterval:60.0]; // 设置请求参数(POST才有请求体,必设) [request setHTTPBody:_data]; // 参数cachePolicy NSURLRequest默认的cache policy是NSURLRequestUseProtocolCachePolicy, 是最能保持一致性的协议。 NSURLRequestReloadIgnoringCacheData 忽略缓存直接从原始地址下载 NSURLRequestReturnCacheDataElseLoad 只有在cache中不存在data时才从原始地址下载 NSURLRequestReturnCacheDataDontLoad 允许app确定是否要返回cache数据,如果使用这种协议当本地不存在response的时候,创建NSURLConnection or NSURLDownload实例时将会马上返回nil;这类似于离线模式,没有建立网络连接; ~~~ **6.同步--->GET请求** ~~~ - (void)onClickBoysPostButton{ // 要求的地址 NSString * urlString = @"http://project.lanou3g.com/teacher/yihuiyun/phpXML.php?sex=男"; // 将地址编码(把 sex=男 转换成 sex=%E7%94%B7) NSString *str = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // 1.将网址字符串封装为NSURL对象 NSURL * url = [NSURL URLWithString:str]; // 网络请求设置 NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; // 请求方式默认是GET [request setHTTPMethod:@"GET"]; // 2.连接服务器 // Response对象,用来得到返回后的数据,比如,用statusCode==200 来判断返回正常 NSHTTPURLResponse *response; NSError * error = nil; // 3. 返回数据 NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; // NSData转化NSString 用的是实例方法 NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",string); NSLog(@"%ld",[response statusCode]); [string release]; } ~~~ **7.同步--->POST请求** ~~~ - (void)onClickPostButton{ //@第一步,创建URL NSString * urlString = @"http://ipad-bjwb.bjd.com.cn/DigitalPublication/publish/Handler/APINewsList.ashx"; NSURL * url = [NSURL URLWithString:urlString]; //@第二步,创建请求 NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; // 设置请求方式POST,默认为GET [request setHTTPMethod:@"POST"]; // 设置参数 NSString * parameterString = @"date=20131129&startRecord=2&len=20&udid=1234567890&terminalType=Iphone&cid=213"; NSData * postData = [parameterString dataUsingEncoding:NSUTF8StringEncoding]; // 客户端向服务器提交数据,都是NSData类型,放在请求体里面 [request setHTTPBody:postData]; //@第三步,连接服务器 NSURLResponse * response = nil; NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]; // NSData转化NSString 用的是实例方法 NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; } ~~~ **8.异步(代理方法,可以监测过程)--->POST请求(这里只展示POST请求,GET请求类似)** ~~~ - (void)onClickPostButton{ //@第一步,创建URL NSString * urlString = @"http://ipad-bjwb.bjd.com.cn/DigitalPublication/publish/Handler/APINewsList.ashx"; NSURL * url = [NSURL URLWithString:urlString]; //@第二步,创建请求 NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; // 设置请求方式POST,默认为GET [request setHTTPMethod:@"POST"]; // 设置参数 NSString * parameterString = @"date=20131129&startRecord=2&len=20&udid=1234567890&terminalType=Iphone&cid=213"; NSData * postData = [parameterString dataUsingEncoding:NSUTF8StringEncoding]; // 客户端向服务器提交数据,都是NSData类型,放在请求体里面 [request setHTTPBody:postData]; //@第三步,连接服务器(异步,同步太坑爹) // 实现2个协议 [NSURLConnection connectionWithRequest:request delegate:self]; } // 服务器开始给客户端回传数据,这个方法只会执行一次 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ // 服务器开始回传数据,客户端需要创建一个空的,可变的Data对象,用于存储每次获取的数据片段 // @property (nonatomic,retain)NSMutableData * returnInfoData; self.returnInfoData = [[NSMutableData alloc]init]; NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response; // 状态码 NSLog(@"%ld",httpResponse.statusCode); // 响应报头 NSLog(@"%@",httpResponse.allHeaderFields); NSLOG_FUNCTION; } // 客户端持续接收数据,data是数据片段,整个数据分段返回,这个方法执行的次数取决于数据总长度[response expectedContentLength] - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [_returnInfoData appendData:data]; NSLOG_FUNCTION; } // 数据完全下载成功,接收到完整数据 - (void)connectionDidFinishLoading:(NSURLConnection *)connection{ // _returnInfoData 是完整的数据了 [_returnInfoData release]; NSLOG_FUNCTION; } // 数据下载失败 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"didFailWithError"); NSLOG_FUNCTION; } ~~~ **9.异步(Blcok方法,不能监测过程)--->POST请求(这里只展示POST请求,GET请求类似)** ~~~ - (void)onClickAsyncBlockButton{ //@第一步,创建URL NSString * urlString = @"http://ipad-bjwb.bjd.com.cn/DigitalPublication/publish/Handler/APINewsList.ashx"; NSURL * url = [NSURL URLWithString:urlString]; //@第二步,创建请求 NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; // 设置请求方式POST,默认为GET [request setHTTPMethod:@"POST"]; // 设置参数 NSString * parameterString = @"date=20131129&startRecord=2&len=20&udid=1234567890&terminalType=Iphone&cid=213"; NSData * postData = [parameterString dataUsingEncoding:NSUTF8StringEncoding]; // 客户端向服务器提交数据,都是NSData类型,放在请求体里面 [request setHTTPBody:postData]; //@第三步,连接服务器(异步,同步太坑爹) // sendAsynchronousRequest 异步接受数据 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { // 数据已经接收完成,进行设置 }]; } ~~~
';

iOS多线程编程(三)Grand Central Dispatch(GCD)详解

最后更新于:2022-04-01 22:59:05

**1.基本介绍:** (1) Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。在Mac OS X 10.6雪豹中首次推出,并在最近引入到了iOS4.0。 (2) GCD是一个替代诸如NSThread等技术的很高效和强大的技术。GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题。 (3) 它是IOS多线程抽象层次最高的一层,下面还有前面2章节介绍的更加轻量级的Cocoa operations,和Thread。 **2.主要方法:** (1)创建一个队列      dispatch_queue_t queue = dispatch_queue_create("LoadImage", NULL);        其中,第一个参数是标识队列的,第二个参数是用来定义队列的参数(目前不支持,因此传入NULL)。 (2)执行一个队列(async--->异步,sync--->同步)      dispatch_async(queue, ^{ [self doSomething]; });         其中,首先传入之前创建的队列,然后提供由队列运行的代码块。 (3)声明并执行一个队列 (如果不需要保留要运行的队列的引用)   dispatch_async(dispatch_queue_create ("LoadImage", NULL), ^{ [self doSomething]; }); (4)暂停一个队列      dispatch_suspend(queue); (5)恢复一个队列(如果暂停一个队列不要忘记恢复)        dispatch_resume(queue); (6)将代码块中的工作转回到主线程(注意,dispatch_suspend (以及dispatch_resume)在主线程上不起作用)   dispatch_sync(dispatch_get_main_queue(), ^{ [self dismissLoginWindow]; }); **3.代码示例** ~~~ #pragma mark - GCD(Grand Central Dispatch) - (void)GCD{ dispatch_queue_t queue = dispatch_queue_create("test", NULL); // 多一个a,异步 dispatch_async(queue, ^{ for (int i = 0; i < 100; i++) { NSLog(@"--多线程--%d",i); } // 判断是否是在多线程运行环境 BOOL isMulti = [NSThread isMultiThreaded]; NSLog(@"%d",isMulti); if (isMulti) { NSLog(@"*********多线程***********"); } // 将代码块中的工作转回到主线程 dispatch_sync(dispatch_get_main_queue(), ^{ // 判断是否是在主线程运行环境 BOOL isMain = [NSThread isMainThread]; if (isMain) { NSLog(@"*********主线程**********"); } }); }); /* // 通过此方式,还是运行在当前线程 dispatch_sync(queue, ^{ // 主线程 }); */ for (int i = 0; i < 100; i++) { NSLog(@"--主线程--%d",i); } } ~~~ **4.加载网络图片(在多线程加载明显比放在主线程加载快N多)** ~~~ #pragma mark - 给UIImageView写一个类目 @interface UIImageView (WebCach) - (void)setImageWithURL:(NSURL *)url; @end #import "UIImageView+WebCach.h" @implementation UIImageView (WebCach) - (void)setImageWithURL:(NSURL *)url{ dispatch_queue_t queue = dispatch_queue_create("loadImage", NULL); dispatch_async(queue, ^{ NSData * data = [NSData dataWithContentsOfURL:url]; UIImage * image = [UIImage imageWithData:data]; // 加载UI的操作,一般放在主线程进行 dispatch_async(dispatch_get_main_queue(), ^{ self.image = image; }); }); } @end - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; [imageView setImageWithURL:[NSURL URLWithString:@"http://www.baidu.com/img/bdlogo.gif"]]; [self.view addSubview:imageView]; } ~~~ **5.最后,引用别人博客对几个方法的介绍** @[dispatch对象](http://blog.csdn.net/lengshengren/article/details/12905747) ~~~ 原文地址 http://www.cnblogs.com/sunfrog/p/3243230.html 谈起iOS的dispatch(正式称谓是Grand Central Dispatch或GCD),不得不说这又是iOS(包括MacOSX)平台的创新,优缺点这里不讨论,只有当你使用时才能真正体会到。我们说dispatch函数的主要目的是实现多任务并发代码,那么要理解dispatch函数,先来了解dispatch对象的定义。 dispatch对象类型的部分定义,主要使用C语言的宏定义: 文件: #define OS_OBJECT_CLASS(name) OS_##name #define OS_OBJECT_DECL(name, ...) \ @protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \ @end \ typedef NSObject *name##_t #define OS_OBJECT_DECL_SUBCLASS(name, super) \ OS_OBJECT_DECL(name, ) 文件: #define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object) #define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object)) OS_OBJECT_DECL(dispatch_object); //定义dispatch_object_t 文件(dispatch队列类定义,其它dispatch对象类似): DISPATCH_DECL(dispatch_queue); //定义dispatch_queue_t 可以通过Xcode预编译后可以看到最终结果,最终定义的都是NSObject类,虽然它们之间没用直接继承关系,但都实现OS_dispatch_object接口,这样dispatch_queue_t对象也同样是dispatch_object_t的对象了。下面就是预编译dispatch_object_t和dispatch_queue_t的结果: @protocol OS_dispatch_object @end typedef NSObject *dispatch_object_t; @protocol OS_dispatch_queue @end typedef NSObject *dispatch_queue_t; 由于dispatch api接口定义成C函数的形式,dispatch的对象都是由C函数形式的厂方法得到(不能继承dispatch类,不用alloc),这样做隐藏dispatch对象的具体形态,把注意力放在如何调用dispatch api上。 从上面dispatch对象宏定义可以看到dispatch对象类的名称一般为dispatch_xyz_t(严格来讲是对象指针),它们都可以看成dispatch_object_t的子类(对象指针),所以使用dispatch对象时套用这个概念就行。 有关dispatch对象的基本接口如下: void dispatch_retain(dispatch_object_t object); //替代dispatch对象常规的retain来持有对象,但ARC编程中不再允许 void dispatch_release(dispatch_object_t object); //替代dispatch对象常规的release来释放对象,同样ARC编程中不再允许 void dispatch_set_context(dispatch_object_t object, void *context); //给dispatch对象绑定特定数据对象(类似线程的TLS数据),会被传给dispatch对象的finalizer函数 void *dispatch_get_context(dispatch_object_t object); //返回dispatch对象绑定的数据对象指针 void dispatch_set_finalizer_f(dispatch_object_t object, dispatch_function_t finalizer); //设置dispatch对象的finalizer函数,当该对象释放时会调用finalizer,部分代码解释如何使用这个函数(ARC模式): dispatch_object_t dispatchObject = ...; void *context = ...; dispatch_set_context(dispatchObject, context); dispatch_set_finalizer_f(dispatchObject, finalizer); ...... dispatchObject = nil; //dispatchObject被释放,这时调用finalizer函数 ...... void finalizer(void *context) {   //处理或释放context相关资源 } dispatch对象的另外两个接口是: void dispatch_resume(dispatch_object_t object); //激活(启动)在dispatch对象上的block调用,可以运行多个block void dispatch_suspend(dispatch_object_t object); //挂起(暂停)在dispatch对象上的block调用,已经运行的block不会停止 一般这两个函数的调用必须成对,否则运行会出现异常。 至此你是否发现这两个函数有些与众不同呢?好像从来没有这么使用对象的,启动对象--暂停对象,呵呵。这正是理解dispatch对象的关键所在。dispatch对象其实是抽象的任务,把动态的任务变成对象来管理。任务是动态的,不存在继承关系,这就是为什么GCD没有提供静态继承dispatch对象类的方式。如果能这样理解,那么在使用dispatch函数时就能够更灵活地去编写代码,实现各种并发的多任务代码。 ~~~ @[dispatch队列](http://blog.csdn.net/lengshengren/article/details/12905787) ~~~ GCD编程的核心就是dispatch队列,dispatch block的执行最终都会放进某个队列中去进行,它类似NSOperationQueue但更复杂也更强大,并且可以嵌套使用。所以说,结合block实现的GCD,把函数闭包(Closure)的特性发挥得淋漓尽致。 dispatch队列的生成可以有这几种方式: 1. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.serial", DISPATCH_QUEUE_SERIAL); //生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。 2. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.concurrent", DISPATCH_QUEUE_CONCURRENT); //生成一个并发执行队列,block被分发到多个线程去执行 3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的。 官网文档解释说共有三个并发队列,但实际还有一个更低优先级的队列,设置优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND。Xcode调试时可以观察到正在使用的各个dispatch队列。 4. dispatch_queue_t queue = dispatch_get_main_queue(); //获得主线程的dispatch队列,实际是一个串行队列。同样无法控制主线程dispatch队列的执行继续或中断。 接下来我们可以使用dispatch_async或dispatch_sync函数来加载需要运行的block。 dispatch_async(queue, ^{   //block具体代码 }); //异步执行block,函数立即返回 dispatch_sync(queue, ^{   //block具体代码 }); //同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。 实际编程经验告诉我们,尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁。 如果queue1是一个串行队列的话,这段代码立即产生死锁: dispatch_sync(queue1, ^{ dispatch_sync(queue1, ^{     ......   });   ......  }); 不妨思考下,为什么下面代码也肯定死锁: dispatch_sync(dispatch_get_main_queue(), ^{   ...... }); 那实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{   //子线程中开始网络请求数据   //更新数据模型   dispatch_sync(dispatch_get_main_queue(), ^{     //在主线程中更新UI代码   }); }); 程序的后台运行和UI更新代码紧凑,代码逻辑一目了然。 dispatch队列是线程安全的,可以利用串行队列实现锁的功能。比如多线程写同一数据库,需要保持写入的顺序和每次写入的完整性,简单地利用串行队列即可实现: dispatch_queue_t queue1 = dispatch_queue_create("com.dispatch.writedb", DISPATCH_QUEUE_SERIAL); - (void)writeDB:(NSData *)data {   dispatch_async(queue1, ^{     //write database   }); } 下一次调用writeDB:必须等到上次调用完成后才能进行,保证writeDB:方法是线程安全的。 dispatch队列还实现其它一些常用函数,包括: void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); //重复执行block,需要注意的是这个方法是同步返回,也就是说等到所有block执行完毕才返回,如需异步返回则嵌套在dispatch_async中来使用。多个block的运行是否并发或串行执行也依赖queue的是否并发或串行。 void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); //这个函数可以设置同步执行的block,它会等到在它加入队列之前的block执行完毕后,才开始执行。在它之后加入队列的block,则等到这个block执行完毕后才开始执行。 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); //同上,除了它是同步返回函数 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); //延迟执行block 最后再来看看dispatch队列的一个很有特色的函数: void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue); 它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源(以后博文会介绍)。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB: dispatch_set_target_queue(dispatchA, dispatchB); 那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行: dispatch_suspend(dispatchA); 则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。 这里只简单举个例子,说明dispatch队列运行的灵活性,在实际应用中你会逐步发掘出它的潜力。 dispatch队列不支持cancel(取消),没有实现dispatch_cancel()函数,不像NSOperationQueue,不得不说这是个小小的缺憾。 ~~~ @[dispatch源](http://blog.csdn.net/lengshengren/article/details/12905811) ~~~ 原文地址 http://www.cnblogs.com/sunfrog/p/3243230.html dispatch源(dispatch source)和RunLoop源概念上有些类似的地方,而且使用起来更简单。要很好地理解dispatch源,其实把它看成一种特别的生产消费模式。dispatch源好比生产的数据,当有新数据时,会自动在dispatch指定的队列(即消费队列)上运行相应地block,生产和消费同步是dispatch源会自动管理的。 dispatch源的使用基本为以下步骤: 1. dispatch_source_t source = dispatch_source_create(dispatch_source_type, handler, mask, dispatch_queue); //创建dispatch源,这里使用加法来合并dispatch源数据,最后一个参数是指定dispatch队列 2. dispatch_source_set_event_handler(source, ^{ //设置响应dispatch源事件的block,在dispatch源指定的队列上运行   //可以通过dispatch_source_get_data(source)来得到dispatch源数据 }); 3. dispatch_resume(source); //dispatch源创建后处于suspend状态,所以需要启动dispatch源 4. dispatch_source_merge_data(source, value); //合并dispatch源数据,在dispatch源的block中,dispatch_source_get_data(source)就会得到value。 是不是很简单?而且完全不必编写同步的代码。比如网络请求数据的模式,就可以这样来写: dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0)); dispatch_source_set_event_handler(source, ^{ dispatch_sync(dispatch_get_main_queue(), ^{     //更新UI }); }); dispatch_resume(source); dispatch_async(dispatch_get_global_queue(0, 0), ^{    //网络请求 dispatch_source_merge_data(source, 1); //通知队列 }); dispatch源还支持其它一些系统源,包括定时器、监控文件的读写、监控文件系统、监控信号或进程等,基本上调用的方式原理和上面相同,只是有可能是系统自动触发事件。比如dispatch定时器: dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 10*NSEC_PER_SEC, 1*NSEC_PER_SEC); //每10秒触发timer,误差1秒 dispatch_source_set_event_handler(timer, ^{   //定时处理 }); dispatch_resume(timer); 其它情况的dispatch源就不再一一举例,可参看官网有具体文档: https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html#//apple_ref/doc/uid/TP40008091-CH103-SW1 最后,dispatch源的其它一些函数大致罗列如下: uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第二个参数 unsignedlong dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第三个参数 void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件处理--即不再调用block。如果调用dispatch_suspend只是暂停dispatch源。 long dispatch_source_testcancel(dispatch_source_t source); //检测是否dispatch源被取消,如果返回非0值则表明dispatch源已经被取消 void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler); //dispatch源取消时调用的block,一般用于关闭文件或socket等,释放相关资源 void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t registration_handler); //可用于设置dispatch源启动时调用block,调用完成后即释放这个block。也可在dispatch源运行当中随时调用这个函数。 ~~~ @[dispatch同步](http://blog.csdn.net/lengshengren/article/details/12905821) ~~~ 原文地址 http://www.cnblogs.com/sunfrog/p/3243230.html GCD提供两种方式支持dispatch队列同步,即dispatch组和信号量。 一、dispatch组(dispatch group) 1. 创建dispatch组 dispatch_group_t group = dispatch_group_create(); 2. 启动dispatch队列中的block关联到group中 dispatch_group_async(group, queue, ^{   // 。。。 }); 3. 等待group关联的block执行完毕,也可以设置超时参数 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 4. 为group设置通知一个block,当group关联的block执行完毕后,就调用这个block。类似dispatch_barrier_async。 dispatch_group_notify(group, queue, ^{   // 。。。 }); 5. 手动管理group关联的block的运行状态(或计数),进入和退出group次数必须匹配 dispatch_group_enter(group); dispatch_group_leave(group); 所以下面的两种调用其实是等价的, A) dispatch_group_async(group, queue, ^{   // 。。。 }); B) dispatch_group_enter(group); dispatch_async(queue, ^{   //。。。   dispatch_group_leave(group); }); 所以,可以利用dispatch_group_enter、 dispatch_group_leave和dispatch_group_wait来实现同步,具体例子:http://stackoverflow.com/questions/10643797/wait-until-multiple-operations-executed-including-completion-block-afnetworki/10644282#10644282。 二、dispatch信号量(dispatch semaphore) 1. 创建信号量,可以设置信号量的资源数。0表示没有资源,调用dispatch_semaphore_wait会立即等待。 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 2. 等待信号,可以设置超时参数。该函数返回0表示得到通知,非0表示超时。 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 3. 通知信号,如果等待线程被唤醒则返回非0,否则返回0。 dispatch_semaphore_signal(semaphore); 最后,还是回到生成消费者的例子,使用dispatch信号量是如何实现同步: dispatch_semaphore_t sem = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //消费者队列 while (condition) {     if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC))) //等待10秒       continue;     //得到数据   } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //生产者队列 while (condition) {     if (!dispatch_semaphore_signal(sem))     {       sleep(1); //wait for a while       continue;     }     //通知成功   } }); ~~~
';

iOS多线程编程(二)NSOperationQueue

最后更新于:2022-04-01 22:59:02

~~~ #pragma mark - NSBlockOperation - (void)test4{ NSOperationQueue * threadQueue = [[NSOperationQueue alloc] init]; // Block真心方便 [threadQueue addOperationWithBlock:^{ for (int i =0; i <100; i++) { NSLog(@"--多线程--%d",i); } }]; for (int i =0; i <100; i++) { NSLog(@"--主线程--%d",i); } [threadQueue release]; } #pragma mark - NSInvocationOperation - (void)test5{ //创建一个线程队列 NSOperationQueue * threadQueue = [[NSOperationQueue alloc]init]; //设置线程执行的并发数( -1 代表没有限制) threadQueue.maxConcurrentOperationCount =1; //创建一个线程操作对象 NSInvocationOperation * threadOperation = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(mutableThread5)object:nil]; //设置线程操作对象的优先级 threadOperation.queuePriority =NSOperationQueuePriorityVeryHigh; //可添加多个,还有一个添加数组对象的方法 [threadQueue addOperation:threadOperation]; for (int i =0; i <100; i++) { NSLog(@"--主线程--%d",i); } [threadOperation release]; [threadQueue release]; } - (void)mutableThread5{ for (int i =0; i <100; i++) { NSLog(@"--多线程--%d",i); } } @最后看看NSOperationQueue的其它常用方法: - (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; //批量加入执行operation,wait标志是否当前线程等待所有operation结束后,才返回 - (void)addOperationWithBlock:(void (^)(void))block; //相当于加入一个NSBlockOperation执行任务 - (NSArray *)operations; //返回已加入执行operation的数组,当某个operation结束后会自动从这个数组清除 - (NSUInteger)operationCount //返回已加入执行operation的数目 - (void)setSuspended:(BOOL)b; //是否暂停将要执行的operation,但不会暂停已开始的operation - (BOOL)isSuspended; //返回暂停标志 - (void)cancelAllOperations; //取消所有operation的执行,实质是调用各个operation的cancel方法 + (id)currentQueue; //返回当前NSOperationQueue,如果当前线程不是在NSOperationQueue上运行则返回nil + (id)mainQueue; //返回主线程的NSOperationQueue,缺省总是有一个queue ~~~
';

iOS多线程编程(一)NSThread

最后更新于:2022-04-01 22:59:00

## @NSThread的使用 ### 1.显示调用的类为NSThread有两种直接创建方式: **- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument(手动开启)** **+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument(自动开启)** ~~~ 1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil]; 2、NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil]; [myThread start]; ~~~ ### 2\. 不显式创建线程的方法: **通过NSObject的Category方法调用:** **- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; //在主线程中运行方法,wait表示是否阻塞这个方法的调用,如果为YES则等待主线程中运行方法结束。一般可用于在子线程中调用UI方法。** **- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait;** //**在指定线程中执行,但该线程必须具备run loop。** **- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;** //产生新线程。 **3.NSThread的其它一些常用的方法** **1. + (NSThread *)currentThread; //获得当前线程** **2. + (void)sleepForTimeInterval:(NSTimeInterval)ti; //线程休眠** **3. + (NSThread *)mainThread; //主线程,亦即UI线程了** **4. - (BOOL)isMainThread; + (BOOL)isMainThread; //当前线程是否主线程** **5. - (BOOL)isExecuting; //线程是否正在运行** **6. - (BOOL)isFinished; //线程是否已结束** **7.- (void)cancel  // 终止线程循环** **8.- (void)start  // 开启线程循环**
';

数据解析(三)解析JSON—–系统自带NSJSONSerialization 与 第三方JSONKit

最后更新于:2022-04-01 22:58:58

**@JSON**(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript(Standard ECMA-262 3rd Edition - December 1999)的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成 **@JSON 语法规则:** 1.JSON 语法是 JavaScript 对象表示语法的子集。    数据在名称/值对中    数据由逗号分隔    花括号保存对象    方括号保存数组    JSON 名称/值对 2.JSON 数据的书写格式是:名称/值对。    名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:"firstName":"John" 3.JSON 值可以是:    数字(整数或浮点数)    字符串(在双引号中)    逻辑值(true 或 false)    数组(在方括号中)    对象(在花括号中)    null **@JSON建构有两种结构:** json简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构 1、对象:对象在js中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,...}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值,这个属性值的类型可以是 数字、字符串、数组、对象几种。 2、数组:数组在js中是中括号“[]”括起来的内容,数据结构为 ["java","javascript","vb",...],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是 数字、字符串、数组、对象几种。 经过对象、数组2种结构就可以组合成复杂的数据结构了。 **@基础示例**     ---整体是个对象,对象的key是"programmers",value是一个数组,数组里面又包含3个对象 {"programmers":[ {"firstName":"Brett","lastName":"McLaughlin","email":"aaaa"}, {"firstName":"Jason","lastName":"Hunter","email":"bbbb"}, {"firstName":"Elliotte","lastName":"Harold","email":"cccc"} ], "authors":[ {"firstName":"Isaac","lastName":"Asimov","genre":"sciencefiction"}, {"firstName":"Tad","lastName":"Williams","genre":"fantasy"}, {"firstName":"Frank","lastName":"Peretti","genre":"christianfiction"} ], "musicians":[ {"firstName":"Eric","lastName":"Clapton","instrument":"guitar"}, {"firstName":"Sergei","lastName":"Rachmaninoff","instrument":"piano"} ]} **@具体形式:** 1、对象是一个无序的“‘名称/值’对”集合。 (1)一个对象以“{”(左括号)开始,“}”(右括号)结束。 (2)每个“名称”后跟一个“:”(冒号); (3)“‘名称/值’ 对”之间使用“,”(逗号)分隔。(如图所示,图中表示数据的方式是类似非确定性[自动机](http://baike.baidu.com/view/409351.htm)的形式,没学过[编译原理](http://baike.baidu.com/view/29903.htm)的人,可能理解起来困难点,实际上也是[正则表达式](http://baike.baidu.com/view/94238.htm)的形式。下同) 例子:表示人的一个对象: { "姓名":"大憨", "年龄":24 } 2、数组是值(value)的有序集合。 (1)一个数组以“[”(左中括号)开始,“]”(右中括号)结束。 (2)值之间使用“,”(逗号)分隔。 例子:一组学生 { "学生": [ {"姓名":"小明","年龄":23}, {"姓名":"大憨","年龄":24} ] } 说明:此Json对象包括了一个学生数组,而学生数组中的值又是两个Json对象。 3、值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。 4、[字符](http://baike.baidu.com/view/263416.htm)串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。 字符串(string)与C或者Java的字符串非常相似。 5、数值(number)也与C或者Java的数值非常相似。除去未曾使用的[八进制](http://baike.baidu.com/view/234126.htm)与十六进制格式。除去一些编码细节。 **@系统提供的JSON解析类----NSJSONSerialization** ~~~ #pragma mark - 系统提供的JSON解析类 - (void)onClickSystem{ // 加载一个本地的JSON文件路径,放到NSData对象中去 NSData * jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Person" ofType:@"txt"]]; NSError * error = nil; // 把一个json格式的data,解析为OC中的数据对象(一般为数组或字典) id obj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; // 判断一下,id是NSMutableArray类型还是NSMutableDictionary if ([obj isKindOfClass:[NSArray class]]) { NSMutableArray * array = obj; NSLog(@"*************%@",array); }else{ NSMutableDictionary * dic = obj; NSLog(@"???????????????%@",dic); } // 把一个OC中的数据对象(数组或对象--在OC中类似字典)转换成JSON格式的数据 NSDictionary * dic = @{@"to": @"壮壮",@"content":@"晚上见",@"from": @"晓宇",@"data": @"2014.04.18"}; NSLog(@"DIC = %@",dic); NSData * data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error]; // 把一个json格式的data,解析为OC中的NSString类型对象 // 无特殊意义,一般用来查看JSON文本封装成 数组 还是 对象(OC中为字典) NSString * jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"-----------------------------%@",jsonString); } ~~~ **@第三方类----JSONKit** ~~~ #pragma mark - 第三方类JSONKit - (void)onClickJSONKit{ #pragma mark 把JSON格式的数据转换成OC中的数据对象 // 从文档中读取json格式的Data NSData * jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Person" ofType:@"txt"]]; // json格式的Data转化为OC对象 id obj1 = [jsonData objectFromJSONData]; NSLog(@"%@",obj1); // 从文档中读取json格式的String NSString * jsonString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Person" ofType:@"txt"] encoding:NSUTF8StringEncoding error:nil]; // json格式的String转化为OC对象 id obj2 = [jsonString objectFromJSONString]; NSLog(@"%@",obj2); #pragma mark 把一个OC中的数据对象转换成JSON格式的数据 NSDictionary * dic = @{@"to": @"壮壮",@"content":@"晚上见",@"from": @"晓宇",@"data": @"2014.04.18"}; // 转化为json格式的Data NSData * dataJson = [dic JSONData]; // 转化为json格式的String NSString * stringJson = [dic JSONString]; } ~~~ @代码样例: ~~~ { "country": { "province": { "-number": "3", "-name": "河北", "city": [ { "-number": "9", "-name": "张家口市", "area": [ { "-number": "146", "#text": "桥西区" }, { "-number": "147", "#text": "桥东区" }, { "-number": "148", "#text": "宣化区" }, { "-number": "149", "#text": "下花园区" }, { "-number": "150", "#text": "宣化县" }, { "-number": "151", "#text": "张北县" }, { "-number": "152", "#text": "康保县" }, { "-number": "153", "#text": "沽源县" }, { "-number": "154", "#text": "尚义县" }, { "-number": "162", "#text": "崇礼县" } ] }, { "-number": "10", "-name": "承德市", "area": [ { "-number": "163", "#text": "双桥区" }, { "-number": "164", "#text": "双滦区" }, { "-number": "165", "#text": "鹰手营子矿区" }, { "-number": "166", "#text": "承德县" }, { "-number": "167", "#text": "兴隆县" }, { "-number": "168", "#text": "平平县" }, { "-number": "169", "#text": "滦平县" } ] }, { "-number": "11", "-name": "沧州市", "area": [ { "-number": "174", "#text": "运河区" }, { "-number": "175", "#text": "新华区" }, { "-number": "176", "#text": "泊头区" }, { "-number": "177", "#text": "任丘区" }, { "-number": "185", "#text": "肃宁县" }, { "-number": "186", "#text": "南皮县" } ] }, { "-number": "12", "-name": "廊坊市", "area": [ { "-number": "190", "#text": "安次区" }, { "-number": "191", "#text": "广阳区" }, { "-number": "192", "#text": "霸州市" }, { "-number": "193", "#text": "三河市" }, { "-number": "194", "#text": "固安县" }, { "-number": "195", "#text": "永清县" } ] }, { "-number": "13", "-name": "衡水市", "area": [ { "-number": "202", "#text": "桃城区" }, { "-number": "203", "#text": "冀州市" }, { "-number": "204", "#text": "深州市" }, { "-number": "205", "#text": "枣强县" }, { "-number": "206", "#text": "武邑县" } ] } ] } } } ~~~ ~~~ #pragma mark - 系统类NSJSONSerialization - (void)initializeAllDataOfNSJSONSerialization{ self.allProvinceArray = [NSMutableArray array]; NSData * jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"hebei" ofType:@"txt"]]; NSError * error = nil; id obj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; NSMutableDictionary * countryDic = obj; NSDictionary * countryInfo = [countryDic objectForKey:@"country"]; NSDictionary * provinceInfo = [countryInfo objectForKey:@"province"]; NSString * pName = [provinceInfo objectForKey:@"-name"]; NSString * pNumber = [provinceInfo objectForKey:@"-number"]; HMTProvince * province = [[HMTProvince alloc]initWithName:pName withNumber:pNumber]; [self.allProvinceArray addObject:province]; NSArray * cityArray = [provinceInfo objectForKey:@"city"]; for (int i = 0; i < [cityArray count]; i++) { NSDictionary * cityInfo = [cityArray objectAtIndex:i]; NSString * cName = [cityInfo objectForKey:@"-name"]; NSString * cNumber = [cityInfo objectForKey:@"-number"]; HMTCity * city = [[HMTCity alloc]initWithName:cName withNumber:cNumber]; [province.cityArray addObject:city]; NSArray * areaArray = [cityInfo objectForKey:@"area"]; for (int j = 0; j < [areaArray count]; j++) { NSString * aName = [[areaArray objectAtIndex:j] objectForKey:@"#text"]; NSString * aNumber = [[areaArray objectAtIndex:j] objectForKey:@"-number"]; HMTArea * area = [[HMTArea alloc]initWithName:aName withNumber:aNumber]; [city.areaArray addObject:area]; [area release]; } [city release]; } [province release]; } #pragma mark - 系统类JSONKit - (void)initializeAllDataOfJSONKit{ self.allProvinceArray = [NSMutableArray array]; NSData * jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"hebei" ofType:@"txt"]]; id obj = [jsonData objectFromJSONData]; NSMutableDictionary * countryDic = obj; NSDictionary * countryInfo = [countryDic objectForKey:@"country"]; NSDictionary * provinceInfo = [countryInfo objectForKey:@"province"]; NSString * pName = [provinceInfo objectForKey:@"-name"]; NSString * pNumber = [provinceInfo objectForKey:@"-number"]; HMTProvince * province = [[HMTProvince alloc]initWithName:pName withNumber:pNumber]; [self.allProvinceArray addObject:province]; NSArray * cityArray = [provinceInfo objectForKey:@"city"]; for (int i = 0; i < [cityArray count]; i++) { NSDictionary * cityInfo = [cityArray objectAtIndex:i]; NSString * cName = [cityInfo objectForKey:@"-name"]; NSString * cNumber = [cityInfo objectForKey:@"-number"]; HMTCity * city = [[HMTCity alloc]initWithName:cName withNumber:cNumber]; [province.cityArray addObject:city]; NSArray * areaArray = [cityInfo objectForKey:@"area"]; for (int j = 0; j < [areaArray count]; j++) { NSString * aName = [[areaArray objectAtIndex:j] objectForKey:@"#text"]; NSString * aNumber = [[areaArray objectAtIndex:j] objectForKey:@"-number"]; HMTArea * area = [[HMTArea alloc]initWithName:aName withNumber:aNumber]; [city.areaArray addObject:area]; [area release]; } [city release]; } [province release]; } ~~~
';

数据解析(二)解析XML之GDataXMLNode

最后更新于:2022-04-01 22:58:56

@GDataXMLNode是Google提供的用于XML数据处理的类集。该类集对libxml2-DOM处理方式进行了封装,能对较小或中等的XML文档进行读写操作且支持XPath语法。     1.[http://code.google.com/p/gdata-objectivec-client/source/browse/trunk/Source/XMLSupport/](http://code.google.com/p/gdata-objectivec-client/source/browse/trunk/Source/XMLSupport/),获得GDataXMLNode.h和GDataXMLNode.m文件,进行如下操作将文件导入到Xcode的工程中:     2.将GDataXMLNode.h/m文件添加到工程中;     3.向工程中增加“libxml2.dylib”库;     4.在工程的“Build Settings”页中找到“Header Search Path”项,添加“/usr/include/libxml2”到其路径 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358af7d570.jpg)     5.搜索框中搜索Other linker flags,同样的方式添加-lxml2 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358af99a1d.jpg) **@代码示例:**       首先在工程中新建一个xml文件,作为我们要解析的对象,新建方法是在工程中新建一个Empty的文件,命名为Student.txt(可以命名为.xml,这个随意),然后添加内容: ~~~ 1 胡明涛 123558 2 成风采 122323 3 陈咬金 21313558 4 天天下 56453558 ~~~ ~~~ #pragma mark - GDataXMLNode - (void)onClickGDataXMLButton{ // 获取工程目录的xml文件 NSData * xmlData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Student" ofType:@"txt"]]; // 从文档中读出完整的XML数据,在内存中形成完整的树形结构 NSError * error = nil; GDataXMLDocument * documents = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:&error]; // 取得根节点(element元素)---Students GDataXMLElement * rootEL = [documents rootElement]; // 获取根节点下的节点,返回的数组---student NSArray * studentsArray = [rootEL elementsForName:@"student"]; for (int i = 0; i < [studentsArray count]; i++) { // 获取单个节点对象 GDataXMLElement * student = [studentsArray objectAtIndex:i]; // 获取student节点下name,number,sex,phone节点的值 GDataXMLElement * nameElement = [[student elementsForName:@"name"] lastObject]; GDataXMLElement * sexElement = [[student elementsForName:@"sex"]lastObject]; GDataXMLElement * numberElement = [[student elementsForName:@"number"]lastObject]; GDataXMLElement * phoneElement = [[student elementsForName:@"phone"]lastObject]; NSString * name = [nameElement stringValue]; NSString * number = [numberElement stringValue]; NSString * sex = [sexElement stringValue]; NSString * phone = [phoneElement stringValue]; // 读标签里面的属性(attributeForName) NSString * sign = [[student attributeForName:@"sign"]stringValue]; if (sign) { NSLog(@"----------%@",sign); } NSLog(@"%@,%@,%@,%@",number,name,sex,phone); } #pragma mark 通过路径,取得指定的节点值,所有节点中同一标签的值 // 一个是相对路径,一个是绝对路径-----------(nodesForXPath) //NSArray * nameArray = [rootEL nodesForXPath:@"//name" error:nil]; NSArray * nameArray = [rootEL nodesForXPath:@"student/name" error:nil]; for (int i = 0; i < [nameArray count]; i++) { NSString * name = [[nameArray objectAtIndex:i] stringValue]; NSLog(@"---%@",name); } } ~~~ ~~~ 38849998 115569999 39930000 116279998 41770000 123430000 30670000 104019996 38900001 121629997 26079999 119279998 32930000 115830001 25850000 114949997 23129999 113319999 26579999 106720001 45750000 126769996 20030000 110349998 38029998 114419998 30229999 120169998 31870000 117230003 36119998 114370002 36119998 114370002 ~~~ ~~~ #pragma mark - 解析citys - (void)parseCitys{ NSData * xmlCitysData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"citys" ofType:@"xml"]]; // 从文档中读取完成的XML文档,在内存中形成完整的树形结构 NSError * error = nil; GDataXMLDocument * documents = [[GDataXMLDocument alloc]initWithData:xmlCitysData options:0 error:&error]; // 取得根节点 GDataXMLElement * rootNode = [documents rootElement]; #pragma mark 获取根节点xml_api_reply的数组,包含的是cities NSArray * citiesArray = [rootNode elementsForName:@"cities"]; for (int i = 0; i < [citiesArray count]; i++) { // 取得单个cities节点 GDataXMLElement * cities = [citiesArray objectAtIndex:i]; #pragma mark 获取节点cities的数组,包含的是city NSArray * cityArray = [cities elementsForName:@"city"]; for (int j = 0; j < [cityArray count]; j++) { // 取得单个city节点 GDataXMLElement * city = [cityArray objectAtIndex:j]; #pragma mark city节点下面是3个并行的节点,都是1个,直接通过lastObject来取得想要的值 NSString * name = [[[[city elementsForName:@"name"]lastObject] attributeForName:@"data"] stringValue]; NSLog(@"name = %@",name); NSString * latitude_e6 = [[[city elementsForName:@"latitude_e6"]lastObject] stringValue]; NSLog(@"latitude_e6 = %@",latitude_e6); NSString * longitude_e6 = [[[city elementsForName:@"longitude_e6"]lastObject] stringValue]; NSLog(@"longitude_e6 = %@",longitude_e6); } } } ~~~ @GDataXMLNode方法小结:      最终的数据读出都是在GDataXMLElement对象中读出的,以下方法均为GDataXMLElement类的方法      1、elementsForName方法,取标签名  name标签的名称“name”      2、attributeForName: 取属性结点 再调stringValue即可取到属性值       3、stringValue: 取标签间的字符串值
';

数据解析(一)解析XML之系统自带NSXMLParse类

最后更新于:2022-04-01 22:58:53

@NSXMLParser 实现的是 SAX 方法解析xml文件,只在xml文档中查找特定条件的内容,并且只提取需要的内容。这样做占用内存小,灵活,正好满足我们的需求。他的缺点就是写,遇到标签就读. **1.创建****NSXMLParser** 要使用NSXMLParser要先创建它,设置各种属性,主要用到以下几个方法: initWithContentsOfURL  通过NSURL创建解析器 initWithData                 通过NSData创建解析器 setDelegate                  为解析器定义委托 parse                           运行解析器 ~~~ NSData * xmlData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Student" ofType:@"txt"]]; NSXMLParser * xmlParser = [[NSXMLParser alloc]initWithData:xmlData]; xmlParser.delegate = self; [xmlParser parse]; [xmlParser release]; ~~~ **2.实现协议方法** ~~~ // 开始解析 - (void)parserDidStartDocument:(NSXMLParser *)parser{ NSLOG_FUNCTION; } //解析到某个起始标签 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ NSLog(@"%@",elementName); NSLOG_FUNCTION; } //解析到节点的值 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ NSLOG_FUNCTION; NSLog(@"%@",string); } //解析到某个结束标签 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ NSLog(@"%@",elementName); NSLOG_FUNCTION; } //结束解析 - (void)parserDidEndDocument:(NSXMLParser *)parser{ NSLOG_FUNCTION; } ~~~
';

数据持久化(六)之Using CoreData with MagicalRecord

最后更新于:2022-04-01 22:58:51

第五节里面,我介绍了CoreData的配置和基本的增删改查,可能很多人会觉得用它真繁琐.这里,我再介绍网上大神对它进行了人性化封装的第三方MagicalRecord,正如FMDB对sqlite进行了封装一样,MagicalRecord让你觉得用CoreData很方便.       @基本配置:          1.下载[MagicalRecord](https://github.com/magicalpanda/MagicalRecord),将里面的MagicalRecord文件夹拖入你的工程          2.确定你创建的工程没有勾选"Use Core Data"          3.导入CoreData.frame框架          4.在.pch文件中引入头文件"CoreData+MagicalRecord.h"(只能,必须在这里引用)          @具体操作:           1.初始化(在didFinishLaunchingWithOptions中) ~~~ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; HMTRootViewController *rootVC = [[HMTRootViewController alloc] init]; self.window.rootViewController = rootVC; // 初始化 [MagicalRecord setupCoreDataStackWithStoreNamed:@"MT.sqlite"]; [self.window makeKeyAndVisible]; return YES; } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. [MagicalRecord cleanUp]; } ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358af50e68.jpg)           3.增删改查具体操作 ~~~ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // 初始化一个Person对象 /** * 这里要注意:默认,xcdatamodeld实体描述表名(name)和类名(class)必须保持一致 * 如果name和class不一致,实现MagicalRecord_MOGenerator协议中得entityName方法来改变 @implementation NSManagedObject (MagicalRecord) + (NSString *) MR_entityName; { NSString *entityName; if ([self respondsToSelector:@selector(entityName)]) { entityName = [self performSelector:@selector(entityName)]; } if ([entityName length] == 0) { entityName = NSStringFromClass(self); } return entityName; } */ Person *person = [Person MR_createEntity]; person.name = @"HMT"; person.sex = @"男"; person.age = @25; } // 添加操作 - (void)addDataOperation{ // 文档解释:For any entities to actually be saved / updated / deleted on disk call following method(增加,更新,删除 都要用这个方法来保存数据) [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];; } // 查询操作 - (void)selectDataOperation{ // find数据库中所有的人 NSArray *peoples = [Person MR_findAll]; // find数据库中第一条记录 Person *firPerson = [Person MR_findFirst]; // find数据库中所有name属性为"HMT"的人并按照年龄age排序 NSArray *otherPeoples = [Person MR_findByAttribute:@"name" withValue:@"HMT" andOrderBy:@"age" ascending:YES]; } // 更新操作 - (void)upDataOperation{ // 选定要修改的人 NSArray *persons = [Person MR_findByAttribute:@"name" withValue:@"HMT"]; for (Person *person in persons) { person.name = @"WDQ"; } [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; } // 删除操作 - (void)deleteDataOperation{ // delete数据库中所有人 [Person MR_truncateAll]; [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; // 根据条件delete特定的某个人 NSArray *persons = [Person MR_findByAttribute:@"name" withValue:@"HMT"]; for (Person *person in persons) { [person MR_deleteEntity]; } [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; } ~~~           4.额外配置(取消前缀名和不打印日志信息) ~~~ //If you want to omit the "MR_" prefix to all MagicalRecord realted method calls define following before importing the MagicalRecord header #define MR_SHORTHAND //Turn off MagicalRecord logging, again by defining following before header import #define MR_ENABLE_ACTIVE_RECORD_LOGGING 0 ~~~            @以上只是一些基本操作,其他可以自己去查看头文件
';

数据持久化(五)之CoreData

最后更新于:2022-04-01 22:58:49

@简单的说,Core Data就是可以存储到磁盘的对象图,[...]Core Data可以帮我们做很多任务作.它可以作为软件的整个模型层。它不仅仅在磁盘上存储数据,也把我们需要的数据对象读取到内存中。                                                                                                                               ——Marcus Zarra, Core Data @Core Data是Mac OS X中Cocoa API的一部分,首次在Mac OS X 10.4 Tiger与iOS 3.0系统中出现[2]。它允许按照实体-属性-值模型组织数据,并以XML,二进制文件或SQLite数据文件的格式将其串行化。Core Data允许用户使用代表实体和实体间关系的高层对象来操作数据。它也可以管理串行化的数据,提供对象生存期管理与object graph管理,包括存储。Core Data直接与SQLite交互,避免开发者使用原本的SQL语句,就像Cocoa绑定在模型-视图-控制器设计中做了很多控制器的工作一样,Core Data做了很多数据模型的工作。它的主要任务是负责数据更改的管理,串行化到磁盘,最小化内存占用,以及查询数据。 @http://www.cocoachina.com/iphonedev/sdk/2010/1126/2397.html( 这是官方对于CoreData的说明) @接下来,我就不利用框架自动生成代码,完全自己编写所有的 Core data 相关代码的命令行应用程序来深入讲解 Core data的使用。 ***概念图*** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358ad9eab5.jpg) @添加CoreData框架,导入#import 写代码之前需要了解6个对象: (1)NSManagedObjectContext(被管理的数据上下文) 操作实际内容(操作持久层) 作用:插入数据,查询数据,删除数据 (2)NSManagedObjectModel(被管理的数据模型) 数据库所有表格或数据结构,包含各实体的定义信息 作用:添加实体的属性,建立属性之间的关系 操作方法:视图编辑器,或代码 (3)NSPersistentStoreCoordinator(持久化存储助理) 相当于数据库的连接器 作用:设置数据存储的名字,位置,存储方式,和存储时机 (4)NSManagedObject(被管理的数据记录) 相当于数据库中的表格记录 (5)NSFetchRequest(获取数据的请求) 相当于查询语句 (6)NSEntityDescription(实体结构) 相当于表格结构 (7)后缀为.xcdatamodeld的包 里面是.xcdatamodel文件,用数据模型编辑器编辑 编译后为.momd或.mom文件 1.自定义封装的CoreData管理类**HMTCoreDataManager** **.h** ~~~ #import #import /** * 数据存储成功的代码块 */ typedef void(^HandleSaveSuccessedBlock)(); /** * 数据存储失败的代码块 */ typedef void(^HandleSaveFailedBlock)(NSError *); @interface HMTCoreDataManager : NSObject @property (nonatomic,strong)NSManagedObjectContext *managedObjectContext; // 托管对象上下文 /** * 创建一个单例对象 * * @return 单例对象 */ + (HMTCoreDataManager *)defaultManager; /** * 根据实体描述获得托管对象 * * @param entityName 指定实体描述名字 * @param aClass 要获取的托管对象 * * @return 托管对象实例 */ - (NSManagedObject *)managedObjectWithEntityName:(NSString *)entityName managedObjectClass:(Class)aClass; /** * 数据存储到磁盘中成功和失败响应的方法,参数为2个block * * @param aSuccessedHandler * @param aFailedHandler */ - (void)saveWithSuccessedHandler:(HandleSaveSuccessedBlock)aSuccessedHandler failedHandler:(HandleSaveFailedBlock)aFailedHandler; //  插入数据 - (void)insertCoreData; //  查询 - (NSMutableArray*)selectData:(NSString *)name; //  删除 - (void)deleteData:(NSManagedObject *)object; //  更新 - (void)updateData:(NSString* )newName; @end ~~~ .m ~~~ #import "HMTCoreDataManager.h" @interface HMTCoreDataManager () @property (nonatomic,strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; // 持久化存储协调器 @property (nonatomic,strong) NSManagedObjectModel * managedObjectModel; // 托管对象模型 @end @implementation HMTCoreDataManager static HMTCoreDataManager *manager = nil; + (HMTCoreDataManager *)defaultManager{ @synchronized(self){ if (manager == nil) { manager = [[HMTCoreDataManager alloc] init]; } } /* // 通过GCD创建 static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ manager = [[HMTCoreDataManager alloc] init]; }); */ return manager; } #pragma mark - 属性的方便之处能在get方法中初始化 /** * 很多人对于这个上下文不太理解,开始我也不太理解,查了很多资料,感觉下面这个解释比较通俗易懂 * 托管对象上下文: * 托管对象上下文包含所有的托管对象,这些托管对象已经为提交给数据库准备就绪,在托管对象上下文中,可以添加、修改和删除托管对象, * 这一层相当于应用程序和数据库之间的缓冲区。 */ - (NSManagedObjectContext *)managedObjectContext{ if (_managedObjectContext) { return _managedObjectContext; } _managedObjectContext = [[NSManagedObjectContext alloc] init]; // 为托管对象上下文指定一个持久化存储协调器 [_managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; return _managedObjectContext; } /** * 持久化存储协调器(持久化存储助理) * 持久化存储协调器处理到数据存储的连接,并且包含一些底层信息,像用到数据存储的名字和位置 * 一般我们无需与它直接打交道,上下文已经封装了对它的调用 */ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ if (_persistentStoreCoordinator) { return _persistentStoreCoordinator; } // 初始化一个持久化存储协调器必须依赖NSManagedObjectModel _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; // 存储路径(返回的是NSURL类型)为Documents目录下,以及数据库名称 NSURL *documentURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL *fileURL = [documentURL URLByAppendingPathComponent:@"myClass.sqlite"]; NSError *error = nil; // 加载持久化存储数据(指定持久化存储的数据类型,默认的是NSSQLiteStoreType,即SQLite数据库) [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:fileURL options:nil error:&error]; if (error != nil) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"添加持久化存储失败" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } return _persistentStoreCoordinator; } /** * 托管对象模型 * 数据库所有表格或数据结构包含各实体的定义信息 * 添加实体的属性,建立属性之间的关系 */ - (NSManagedObjectModel *)managedObjectModel{ if (_managedObjectModel) { return _managedObjectModel; } // 获取托管对象模型文件的地址 // 编译器会自动将"xcdatamodeld"格式转化为"momd" NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"myClassModel" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; } - (NSManagedObject *)managedObjectWithEntityName:(NSString *)entityName managedObjectClass:(Class)aClass{ // 创建"HMTClass"的实体描述 NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"HMTClassEntity" inManagedObjectContext:self.managedObjectContext]; // 通过"HMTClass"的实体描述创建HMTClass的托管对象 NSManagedObject *managedObject = [[aClass alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.managedObjectContext]; return managedObject; } - (void)saveWithSuccessedHandler:(HandleSaveSuccessedBlock)aSuccessedHandler failedHandler:(HandleSaveFailedBlock)aFailedHandler{ NSError *error = nil; [self.managedObjectContext save:&error]; if (error != nil) { aFailedHandler(error); }else { aSuccessedHandler(); } } @end ~~~ 2.创建模型文件**ManagedObject****的过程** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358adb7098.jpg) ** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358adf2441.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358ae4fac9.jpg) 3.将创建的模型文件转化为对应的类文件 第一种:command+n ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358ae757f5.jpg) 第二种: 选中myClassModel.xcdatamodeld![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358aebf3ea.jpg) 之后都是一样的--->选中模型文件--->选中要创建的实体 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358aed8340.jpg)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-11_569358af1a6cd.jpg) 最后,就会生成前面图片中的3个类,都是继承了NSManagedObject ~~~ ------------------class------------------------------- @class HMTStudent, HMTTeacher; @interface HMTClass : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSSet *students; @property (nonatomic, retain) HMTTeacher *teacher; @end @interface HMTClass (CoreDataGeneratedAccessors) - (void)addStudentsObject:(HMTStudent *)value; - (void)removeStudentsObject:(HMTStudent *)value; - (void)addStudents:(NSSet *)values; - (void)removeStudents:(NSSet *)values; @end #import "HMTClass.h" #import "HMTStudent.h" #import "HMTTeacher.h" @implementation HMTClass @dynamic name; @dynamic students; @dynamic teacher; @end ------------------teacher------------------------------- @class HMTClass; @interface HMTTeacher : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSString * course; @property (nonatomic, retain) HMTClass *myClass; @end #import "HMTTeacher.h" #import "HMTClass.h" @implementation HMTTeacher @dynamic name; @dynamic course; @dynamic myClass; @end ------------------student------------------------------- @class HMTClass, HMTTeacher; @interface HMTStudent : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSNumber * age; @property (nonatomic, retain) HMTTeacher *teacher; @property (nonatomic, retain) HMTClass *myClass; @end #import "HMTStudent.h" #import "HMTClass.h" #import "HMTTeacher.h" @implementation HMTStudent @dynamic name; @dynamic age; @dynamic teacher; @dynamic myClass; @end ~~~ 4.准备工作都OK了,最后进行数据的持久化存储 ~~~ #pragma mark - 增删改查 // 插入数据操作(类似于FMDB做法,方法后面可带你想存储的数据对象,这里只是单纯的演示) // - (void)insertCoreData:(AppleClass *)appleClass - (void)insertCoreData{ // 创建"HMTClass"的实体描述,传入的正是前面标注的实体描述名 NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"HMTClassEntity" inManagedObjectContext:self.managedObjectContext]; // 通过"HMTClass"的实体描述创建HMTClass的托管对象 HMTClass *hmtClass = [[HMTClass alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.managedObjectContext]; // hmtClass.name = appleClass.name; hmtClass.name = @"Apple"; // 托管对象上下文将托管对象的更改保存到磁盘文件中 // - save 将数据保存到数据库 NSError *error = nil; [self.managedObjectContext save:&error]; if (error != nil) { NSLog(@"添加失败:%@",[error localizedDescription]); } } // 删除数据操作 - (void)deleteData:(NSManagedObject *)object{ [self.managedObjectContext deleteObject:object]; NSError *error = nil; [[HMTCoreDataManager defaultManager].managedObjectContext save:&error]; if (error != nil) { NSLog(@"删除失败:%@",[error localizedDescription]); } } // 查询数据操作(依据特定条件查询,如果是select * 就直接不写谓词判断语句) - (NSMutableArray*)selectData:(NSString *)name{ // 创建指定Entity的查询语句 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"HMTClassEntity"]; // 谓词,原理和用法都类似于SQL中的where,作用相当于数据库的过滤取(我blog中OC分类有具体讲到) NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"name == \'%@\'",name],nil]; fetchRequest.predicate = predicate; // 查询的结果按哪个key进行排序,YES为升序 // NSSortDescriptor *timestampSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"classNO" ascending:YES]; // fetchRequest.sortDescriptors = @[timestampSortDescriptor]; // 注意查询返回的只能是NSArray类型 NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; NSMutableArray *resultArray = [NSMutableArray arrayWithArray:fetchedObjects]; return resultArray; } // 更新数据操作 - (void)updateData:(NSString *)newName{ // 创建指定Entity的查询语句 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"HMTClassEntity"]; // 注意查询返回的只能是NSArray类型 NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; // 更新 for (HMTClass *info in fetchedObjects) { info.name = newName; } //保存 if ([self.managedObjectContext save:nil]) { //更新成功 NSLog(@"更新成功"); } } ~~~
';

数据持久化(四)之NSUserDefaults

最后更新于:2022-04-01 22:58:47

@前面讲了sqlite存储数据,现在来说一个存储类似用户配置(常见的如判断App是否是第一次启动而实现引导页)数据的类----NSUserDefaults NSUserDefault的使用比较简单: NSUserDefaults *mySettingData = [NSUserDefaults standardUserDefaults];   创建NSUserDefaults对象之后即可往里面添加数据,它支持的数据类型有NSString、 NSNumber、NSDate、 NSArray、NSDictionary、BOOL、NSInteger、NSFloat等系统定义的数据类型,如果要存放自定义的对象(如自定义的类对象),则必须将其转换成NSData存储: ~~~ NSArray *arr = [[NSArray alloc] initWithObjects:@"arr1", @"arr2", nil] [mySettingData setObject:arr forKey:@"arrItem"]; [mySettingData setObject:@"admin" forKey:@"user_name"]; [mySettingData setBOOL:@YES forKey:@"auto_login"]; [mySettingData setInteger:1 forKey:@"count"]; ~~~ @往NSUserDefaults添加数据后,它们就变成了全局的变量,App中即可读写NSUserDefault中的数据: ~~~ NSUserDefaults *mySetting = [NSUserDefaults standardUserDefaults]; NSLog(@"arrItem=%@", [mySetting objectForKey:@"arrItem"]); NSLog(@"user_name=%@", [mySetting objectForKey:@"user_name"]); NSLog(@"count=%d", [mySetting integerForKey:@"count"]); ~~~ @如果想删除某个数据项,可以使用removeObjectForKey删除数据: [mySetting removeObjectForKey:@"arrItem"];   @需要注意的是,NSUserDefaults是定时把缓存中的数据写入磁盘的,而不是即时写入,为了防止在写完NSUserDefaults后程序退出导致的数据丢失,可以在写入数据后使用synchronize强制立即将数据写入磁盘: [mySetting synchronize];   运行上面的语句后,NSUserDefaults中的数据即被写入到.plist文件中,如果是在模拟器上运行程序,可以在Mac的/Library/Prefereces目录下面找到一个文件名为com.apple.PeoplePicker.plist的plist文件,用Xcode打开该文件,可以看到刚才写入的数据。 @pragma mark -引导页只执行一次 ~~~ if(![[NSUserDefaults standardUserDefaults] boolForKey:@"firstStart"]){ 这一行-->加入引导页[self startYinDaoYe] [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"firstStart"]; [[NSUserDefaults standardUserDefaults] synchronize]; } ~~~ @系统定义的数据类型,NSUserDefault**配合NSKeyedArchiver和NSKeyedUnarchiver做本地持久化** 这里用数组做个举例: ~~~ static NSString *const archiverKey = @"DataArray"; - (void)_testNSKeyedArchiverAndNSKeyedUnarchiver { NSArray *setDataArray = @[@"1",@"2",@"3"]; NSData *archiverData = [NSKeyedArchiver archivedDataWithRootObject:setDataArray]; [[NSUserDefaults standardUserDefaults] setObject:archiverData forKey:archiverKey]; // 切莫忘记,依旧调用 synchronize 立即写入沙盒中 [[NSUserDefaults standardUserDefaults] synchronize]; NSArray *getDataArray = [NSArray new]; NSData *unarchiverData = [[NSUserDefaults standardUserDefaults] objectForKey:archiverKey]; getDataArray = [NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData]; NSLog(@" getDataArray = %@",getDataArray); } ~~~ @切记,数组类型为NSArray的时候可以直接getDataArray = [NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData]; 如果数组类型是NSMutableArray,则必须,getDataArray = [NSMutableArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData]]; 否则getDataArray为nil **@NSUserDefaults只支持: NSString, NSNumber, NSDate, NSArray, NSDictionary. 如果把一个自定义的类存到一个NSArray里,然后再存到NSUserDefaults里也是不能成功的。 那怎么办呢? 让这个自定义类实现协议中的** **- (id) initWithCoder: (NSCoder *)coder方法** **- (void) encodeWithCoder: (NSCoder *)coder方法,** **然后把该自定义的类对象编码到NSData中,再从NSUserDefaults中进行读取。** **具体可以回看我的-->数据持久化(一)序列化与反序列化**
';

数据持久化(三)使用第三方类库FMDB

最后更新于:2022-04-01 22:58:44

@SQLite是一种小型的轻量级的关系型数据库,在移动设备上使用是非常好的选择,无论是Android还是IOS,都内置了SQLite数据库,现在的版本都是SQLite3。在IOS中使用SQLite如果使用SDK提供的方法,特别麻烦也不利于理解和使用,感觉使用很不方便,今天就讲讲一个针对IOS的SQlite API封装的第三方库FMDB,FMDB对SDK中的API做了一层封装,使之使用OC来访问,使用方便而且更熟悉。FMDB的下载地址[https://github.com/ccgus/fmdb](https://github.com/ccgus/fmdb)。 @FMDB主要涉及两个类,FMDatabase和FMResultSet 下载完FMDB源码后把文件拖到工程中,并导入SQLite支持        库,libsqlite3.0.dylib ~~~ #import "HMTDataBaseHandle.h" #import "FMDB.h" #import "HMTPerson.h" @implementation HMTDataBaseHandle static HMTDataBaseHandle * _dbHandle = nil; + (HMTDataBaseHandle *)shareInstance{ @synchronized(self){ if (!_dbHandle) { _dbHandle = [[HMTDataBaseHandle alloc]init]; [_dbHandle openDataBase]; [_dbHandle createTable]; } } return _dbHandle; } #pragma mark - 定义一个 FMDatabase 对象 static FMDatabase * database = nil; #pragma mark - 获得沙盒文件下Documents路径 - (NSString *)getDocumentsPath{ NSString * documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; return documents; } #pragma mark - 打开数据库操作------ databaseWithPath open - (void)openDataBase{ if (database) { return; } NSString * DataBasePath = [[self getDocumentsPath] stringByAppendingPathComponent:@"test.sqlite"]; database = [FMDatabase databaseWithPath:DataBasePath]; if (![database open]) { NSLog(@"打开数据库失败"); } // 为数据库设置缓存,提高查询效率 database.shouldCacheStatements = YES; NSLog(@"打开数据库成功"); } #pragma mark - 关闭数据库操作 - (void)closeDataBase{ if (![database close]) { NSLog(@"关闭数据库失败"); return; } database = nil; NSLog(@"关闭数据库成功"); } #pragma mark - 管理创建表的操作 - (void)createTable{ [self openDataBase]; // 判断表是否存在,不存在就创建------ tableExists if (![database tableExists:@"Person"]) { [database executeUpdate:@"CREATE TABLE Person(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)"]; NSLog(@"创建表成功"); } [self closeDataBase]; } #pragma mark - 增加数据操作----- executeUpdate - (void)insertIntoDataBase:(HMTPerson *)person{ [self openDataBase]; [database executeUpdate:@" INSERT INTO Person (id,name,age) VALUES (?,?,?)",[NSString stringWithFormat:@"%i",person.personId],person.personName,[NSString stringWithFormat:@"%i",person.personAge]]; [self closeDataBase]; } #pragma mark - 删除数据操作----- executeUpdate - (void)deleteDataFromDataBase:(HMTPerson *)person{ [self openDataBase]; [database executeUpdate:@" DELETE FROM Person WHERE id = ?",[NSString stringWithFormat:@"%i",person.personId]]; [self closeDataBase]; } #pragma mark - 更新数据操作----- executeUpdate - (void)updateFromDataBase:(HMTPerson *)person{ [self openDataBase]; [database executeUpdate:@" UPDATE Person SET name = ? WHERE id = ?",person.personName,[NSString stringWithFormat:@"%i",person.personId]]; [self closeDataBase]; } #pragma mark - 查询数据操作(与其他的都不一样,查询是调用executeQuery,切记切记!!!!!!) - (void)selectAllDataFromDataBase{ [self openDataBase]; FMResultSet * resultSet = [database executeQuery:@" SELECT * FROM Person"]; while ([resultSet next]) { int Id = [resultSet intForColumn:@"id"]; NSString * name = [resultSet stringForColumn:@"name"]; int age = [resultSet intForColumn:@"age"]; NSLog(@"personId:%i,personName:%@,personAge:%i",Id,name,age); } [self closeDataBase]; } @end ~~~ @FMDB提供如下多个方法来获取不同类型的数据: 1.intForColumn: 2.longForColumn: 3.longLongIntForColumn: 4.boolForColumn: 5.doubleForColumn: 6.stringForColumn: 7.dateForColumn: 8.dataForColumn: 9.dataNoCopyForColumn: 10.UTF8StringForColumnIndex: 11.objectForColumn: @如果我们的app需要多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安全了。切记不能在多个线程中共同一个FMDatabase对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题。      使用FMDatabaseQueue很简单,首先用一个数据库文件地址来初使化FMDatabaseQueue,然后就可以将一个闭包(block)传入inDatabase方法中。在闭包中操作数据库,而不直接参与FMDatabase的管理。 ~~~ -(void)executeDBOperation { //获取Document文件夹下的数据库文件,没有则创建 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *dbPath = [docPath stringByAppendingPathComponent:@"user.db"]; FMDatabaseQueue *databaseQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath]; [databaseQueue inDatabase:^(FMDatabase *db){ [db executeUpdate:@"insert into user values (?,?,?)",@"Ren",@"Male",[NSNumber numberWithInt:20]]; }]; [databaseQueue close]; } ~~~
';

数据持久化(二)—–Sqlite

最后更新于:2022-04-01 22:58:42

**@常用接口(2个重要结构体和5个主要函数)** sqlite3               *pdb, 数据库句柄,跟文件句柄 FILE 很类似 sqlite3_stmt      *stmt, 这个相当于 ODBC 的 Command 对象,用于保存编译好的 SQL 语句 sqlite3_open(),   打开数据库 sqlite3_exec(),   执行非查询的 sql 语句 sqlite3_prepare(), 准备 sql 语句,执行 select 语句或者要使用 parameter bind 时(封装了 sqlite3_exec ) . Sqlite3_step(), 在调用 sqlite3_prepare 后,使用这个函数在记录集中移动。 Sqlite3_close(), 关闭数据库文件 还有一系列的函数,用于从记录集字段中获取数据,如 sqlite3_column_text(), 取 text 类型的数据。 sqlite3_column_blob (),取 blob 类型的数据 sqlite3_column_int(), 取 int 类型的数据 **@Sqlite3主要步骤如下:** 1 首先获取iPhone上Sqlite 3的数据库文件的地址 2 打开Sqlite 3的数据库文件 3 定义SQL文 4 邦定执行SQL所需要的参数 5 执行SQL文,并获取结果 6 释放资源 7 关闭Sqlite 3数据库。 **@导入库**       在工程中的Frameworks中导入相应的libsqlite3.dylib文件,也许在相应的目录下存在多个以libsqlite3开头的文件,务必选择libsqlite3.dylib,它始终指向最新版的SQLite3库的别名。  **@SQLite数据库执行SQL语句的过程 ** 1\. 调用sqlite3_prepare()将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt).该结构体中包含了将要执行的的SQL语句的信息.  2\. 如果需要传入参数,在SQL语句中用'?'作为占位符,再调用sqlite3_bind_XXX()函数将对应的参数传入. 3\. 调用sqlite3_step(),这时候SQL语句才真正执行.注意该函数的返回值,SQLITE_DONE和SQLITE_ROW都是表示执行成功, 不同的是SQLITE_DONE表示没有查询结果,象UPDATE,INSERT这些SQL语句都是返回SQLITE_DONE,SELECT查询语句在 查询结果不为空的时候返回SQLITE_ROW,在查询结果为空的时候返回SQLITE_DONE.  4\. 每次调用sqlite3_step()的时候,只返回一行数据,使用sqlite3_column_XXX()函数来取出这些数据.要取出全部的数据需要 反复调用sqlite3_step(). (注意, 在bind参数的时候,参数列表的index从1开始,而取出数据的时候,列的index是从0开始).  5\. 在SQL语句使用完了之后要调用sqlite3_finalize()来释放stmt占用的内存.该内存是在sqlite3_prepare()时分配的.  6\. 如果SQL语句要重复使用,可以调用sqlite3_reset()来清楚已经绑定的参数 ~~~ #import "HMTSqlDBManager.h" #import //--------记得导入libsqlite3.0.dylib库 @implementation HMTSqlDBManager static HMTSqlDBManager * _dbManager = nil; + (HMTSqlDBManager *)shareInstance{ @synchronized(self){ if (!_dbManager) { _dbManager = [[HMTSqlDBManager alloc]init]; } } return _dbManager; } #pragma mark - 获取沙盒下Documents路径 - (NSString *)documentsPath{ return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; } #pragma mark - 先声明一个数据库类型的变量 static sqlite3 * dbSqlite = nil; #pragma mark - 打开数据库操作 - (void)openDB{ // (3)如果数据库存在并且已经打开,直接返回 if (dbSqlite) { return; } // 拼接出数据库全路径 NSString * filePath = [[self documentsPath] stringByAppendingPathComponent:@"Lanou.sqlite"]; // 创建一个文件管理器 NSFileManager * fileManager = [NSFileManager defaultManager]; // (1)判断是否存在数据库文件 if (![fileManager fileExistsAtPath:filePath]) { // 打开数据库(这里已经会创建data.sqlite数据库了,但是,里面为空) int result = sqlite3_open([filePath UTF8String], &dbSqlite); if (result == SQLITE_OK) { // 设置创建表语句CREATE NSString * sql = @"CREATE TABLE Student (number INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , name TEXT DEFAULT 18, age INTEGER NOT NULL DEFAULT 18,sex TEXT DEFAULT 不男不女,icon BLOB);"; // 执行 sqlite3_exec(dbSqlite, [sql UTF8String], NULL, NULL, NULL); } } // (2)打开数据库 sqlite3_open([filePath UTF8String], &dbSqlite); } #pragma mark - 关闭数据库操作 - (void)closeDB{ int result = sqlite3_close(dbSqlite); if (result == SQLITE_OK) { dbSqlite = nil; } } #pragma mark - **********增-----删-----改-----查 数据,最好封装成对象,直接对对象进行操作********** #pragma mark - 插入数据操作 - (void)insertDataWithNumber:(int)number Name:(NSString *)name Sex:(NSString *)sex Age:(int)age Image:(UIImage *)image{ // 1.打开数据库 [self openDB]; // 2.跟随指针,记录数据库的每步操作 sqlite3_stmt * stmt = nil; // 3.绑定数据,执行sql语句(插入列表,用"?"占位符表示) NSString * sql = @"INSERT INTO Student(number,name,sex,age,icon) VALUES(?,?,?,?,?)"; // 4.编译,验证SQL语句是否正确 int result = sqlite3_prepare_v2(dbSqlite, [sql UTF8String], -1, &stmt, NULL); if (result == SQLITE_OK) { // 5.绑定sql语句中得值(替换"?")----图像存入数据库,自断类型为blob,将图像转为二进制数据NSData存入 sqlite3_bind_int(stmt, 1, number); sqlite3_bind_text(stmt, 2, [name UTF8String], -1, NULL); sqlite3_bind_text(stmt, 3, [sex UTF8String], -1, NULL); sqlite3_bind_int(stmt, 4, age); sqlite3_bind_blob(stmt, 5, [UIImagePNGRepresentation(image) bytes], (int)[UIImagePNGRepresentation(image) length], NULL); } // 6.执行 sqlite3_step(stmt); // 7.释放 sqlite3_finalize(stmt); // 8.关闭数据库 [self closeDB]; } #pragma mark - 修改数据操作 - (void)updateNumber:(int)number Name:(NSString *)name{ // 1. [self openDB]; // 2. sqlite3_stmt * stmt= nil; // 3. NSString * sql = @"UPDATE Student SET name = ? WHERE number = ?"; // 4. int result = sqlite3_prepare_v2(dbSqlite, [sql UTF8String], -1, &stmt, NULL); if (result == SQLITE_OK) { // 5. sqlite3_bind_text(stmt, 1, [name UTF8String], -1, NULL); sqlite3_bind_int(stmt, 2, number); // 6. sqlite3_step(stmt); } // 7. sqlite3_finalize(stmt); // 8. [self closeDB]; } #pragma mark - 删除数据操作 - (void)deleteNumber:(int)number{ // 1. [self openDB]; // 2.stmt跟随指针(数据库句柄) sqlite3_stmt * stmt= nil; // 3. NSString * sql = @"DELETE FROM Student WHERE number = ?"; // 4. int result = sqlite3_prepare_v2(dbSqlite, [sql UTF8String], -1, &stmt, NULL); if (result == SQLITE_OK) { // 5. sqlite3_bind_int(stmt,1, number); // 6. sqlite3_step(stmt); } // 7. sqlite3_finalize(stmt); // 8. [self closeDB]; } #pragma mark - 查询数据操作 - (NSArray *)selectAllData{ NSMutableArray * allDataArray = [NSMutableArray array]; // 1. dbSqlite = [self openDB]; // 2.类似JAVA句柄? sqlite3_stmt * stmt = nil; // 3.设置查询数据语句SELECT NSString * sql = @"SELECT name FROM Student"; // 4.编译 int result = sqlite3_prepare_v2(dbSqlite, [sql UTF8String], -1, &stmt, NULL); if (result == SQLITE_OK) { // 5.查询数据 while (sqlite3_step(stmt) == SQLITE_ROW) { // 6.查询的索引下标从0开始(每一次循环后,下标+1) const unsigned char * name = sqlite3_column_text(stmt, 0); //NSString * nameString = [NSString stringWithUTF8String:(const char *)name]; NSString * nameString = [NSString stringWithCString:(const char *)name encoding:NSUTF8StringEncoding]; [allDataArray addObject:nameString]; } // 8.关闭数据库句柄 sqlite3_finalize(stmt); // 9. [self closeDB]; return allDataArray; } return nil; } - (NSMutableDictionary *)selectOneDataWithNumber:(int)number{ sqlite3 * _dbSqlite = [self openDB]; sqlite3_stmt * stmt = nil; NSString * sql = @"SELECT * FROM Student WHERE number = ?"; int result = sqlite3_prepare_v2(_dbSqlite, [sql UTF8String], -1, &stmt, NULL); if (result == SQLITE_OK) { sqlite3_bind_int(stmt, 1, number); while (sqlite3_step(stmt) == SQLITE_ROW) { int number = sqlite3_column_int(stmt, 0); NSString * name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)]; int age = sqlite3_column_int(stmt, 2); NSString * sex = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3)]; UIImage * image = [UIImage imageWithData:[NSData dataWithBytes:sqlite3_column_blob(stmt, 4) length:sqlite3_column_bytes(stmt, 4)]]; NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:number],@"number",name,@"name",[NSNumber numberWithInt:age],@"age",sex,@"sex",image,@"image", nil]; // 关闭数据库句柄 sqlite3_finalize(stmt); return dic; } } return nil; } @end @SQLite支持哪些数据类型些? NULL 值为NULL INTEGER 值为带符号的整型,根据类别用1,2,3,4,6,8字节存储 REAL 值为浮点型,8字节存储 TEXT 值为text字符串,使用数据库编码(UTF-8, UTF-16BE or UTF-16-LE)存储 BLOB 值为二进制数据,具体看实际输入 ---------但实际上,sqlite3也接受如下的数据类型: smallint 16 位元的整数 interger 32 位元的整数 decimal(p,s) p 精确值和 s 大小的十进位整数,精确值p是指全部有几个数(digits)大小值 ,s是指小数点後有几位数。如果没有特别指定,则系统会设为 p=5; s=0 。 float 32位元的实数。 double 64位元的实数。 char(n) n 长度的字串,n不能超过 254。 varchar(n) 长度不固定且其最大长度为 n 的字串,n不能超过 4000。 graphic(n) 和 char(n) 一样,不过其单位是两个字元 double-bytes, n不能超过127。 这个形态是为了支援两个字元长度的字体,例如中文字。 vargraphic(n) 可变长度且其最大长度为 n 的双字元字串,n不能超过 2000。 date 包含了 年份、月份、日期。 time 包含了 小时、分钟、秒。 timestamp 包含了 年、月、日、时、分、秒、千分之一秒。 @如果将某个字段设置为INTEGER PRIMARY KEY属性,有什么特性? 如果将声明表的一列设置为 INTEGER PRIMARY KEY,则具有: 1.每当你在该列上插入一NULL值时, NULL自动被转换为一个比该列中最大值大1的一个整数; 2.如果表是空的, 将会是1; ---------注意该整数会比表中该列上的插入之前的最大值大1。 该键值在当前的表中是唯一的。但有可能与已从表中删除的值重叠。要 想建立在整个表的生命周期中唯一的键值,需要在 INTEGER PRIMARY KEY 上增加AUTOINCREMENT声明。那么,新的键值将会比该表中曾能存在过的最大值大1。 @字段声明中有AUTOINCREMENT属性,有什么与众不同的含义? 要想建立在整个表的生命周期中唯一的键值,需要在 INTEGER PRIMARY KEY 上增加AUTOINCREMENT声明。那么,新的键值将会比该表中曾能存在过的最大值大1。 @SQL常用命令使用方法: (1) 数据记录筛选: sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]" sql="select * from 数据表 where 字段名 like %字段值% order by 字段名 [desc]" sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]" sql="select * from 数据表 where 字段名 in (值1,值2,值3)" sql="select * from 数据表 where 字段名 between 值1 and 值2" (2) 更新数据记录: sql="update 数据表 set 字段名=字段值 where 条件表达式" sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式" (3) 删除数据记录: sql="delete from 数据表 where 条件表达式" sql="delete from 数据表" (将数据表所有记录删除) (4) 添加数据记录: sql="insert into 数据表 (字段1,字段2,字段3 …) valuess (值1,值2,值3 …)" sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添加到目标数据表) (5) 数据记录统计函数: AVG(字段名) 得出一个表格栏平均值 COUNT(*|字段名) 对数据行数的统计或对某一栏有值的数据行数统计 MAX(字段名) 取得一个表格栏最大的值 MIN(字段名) 取得一个表格栏最小的值 SUM(字段名) 把数据栏的值相加 引用以上函数的方法: sql="select sum(字段名) as 别名 from 数据表 where 条件表达式" set rs=conn.excute(sql) 用 rs("别名") 获取统的计值,其它函数运用同上。 (6) 数据表的建立和删除: CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… ) 例:CREATE TABLE tab01(name varchar(50),datetime default now()) DROP TABLE 数据表名称 (永久性删除一个数据表) @sqlite中用LIMIT返回前2行,语句如下: select * from aa order by ids desc LIMIT 2 ~~~
';

数据持久化(一)—–归档 读写 文件路径

最后更新于:2022-04-01 22:58:40

~~~ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]autorelease]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; /*HMTRootViewController * rootVC = [[HMTRootViewController alloc]init]; self.window.rootViewController = rootVC; [rootVC release];*/ // 1.获取沙盒文件的主路径 NSLog(@"home = %@",NSHomeDirectory()); // 2.获取Documents的路径(获取Library的路径类似) NSLog(@"documents = %@", [NSHomeDirectory() stringByAppendingString:@"/Documents"]); NSLog(@"documents = %@", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]); NSLog(@"documents = %@",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]); // 3.获取tmp的路径(单独的方法,与众不同) NSLog(@"tmp = %@",NSTemporaryDirectory()); // 4.应用程序包.app-----只有读取的权利,没有修改的权利 NSLog(@".app = %@",[[NSBundle mainBundle] bundlePath]); NSLog(@"resource = %@",[[NSBundle mainBundle]resourcePath]); NSLog(@".exec = %@",[[NSBundle mainBundle] executablePath]); //[self simpleOperationOfApp]; [self complexOperationOfApp]; [self complexArrayOperationOfApp]; return YES; } #pragma mark - 简单的数据文件write/read - (void)simpleOperationOfApp{ #pragma mark 字符串---txt // 1.获取存储文件的上一级路径 NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; // 2.拼接一个完整的文件存储路径 NSString * filePath = [documentsPath stringByAppendingPathComponent:@"text.txt"]; // 3.write数据 NSString * saveString = @"我叫胡明涛"; [saveString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; // 4.从文件中read NSString * readString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; NSLog(@"%@",readString); #pragma mark 数组---plist NSString * cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString * arrayFilePath = [cachesPath stringByAppendingPathComponent:@"names.plist"]; NSArray * writeArray = @[@"吴凯",@"许珍珍",@"左友东"]; [writeArray writeToFile:arrayFilePath atomically:YES]; NSArray * readArray = [NSArray arrayWithContentsOfFile:arrayFilePath]; NSLog(@"%@",readArray); #pragma mark 字典---plist NSString * tmpPath = NSTemporaryDirectory(); NSString * dicFilePath = [tmpPath stringByAppendingPathComponent:@"dic.plist"]; NSDictionary * writeDic = @{@"name": @"HMT",@"sex":@"female"}; [writeDic writeToFile:dicFilePath atomically:YES]; NSDictionary * readDic = [NSDictionary dictionaryWithContentsOfFile:dicFilePath]; NSLog(@"%@",readDic); // 修改数据 [readDic setValue:@"humingtao" forKey:@"name"]; [readDic writeToFile:dicFilePath atomically:YES]; NSLog(@"%@",readDic); #pragma mark NSData // 获取图片对象 UIImage * writeImage = [UIImage imageNamed:@"OJ2NMBNXQ1G5_20121120051201760"]; // 图片对象转变为NSData类型 NSData * writeData = UIImagePNGRepresentation(writeImage); NSString * imageFilePath = [documentsPath stringByAppendingPathComponent:@"美女.png"]; [writeData writeToFile:imageFilePath atomically:YES]; NSData * readData = [NSData dataWithContentsOfFile:imageFilePath]; // imageNamed------图片会存入缓存,下次加载很迅速,但是耗内存,图片一大就嗝屁了 UIImage * readImage = [UIImage imageWithData:readData]; // 从文件路径中找,调用一次就重新加载一次,只是显示图片,不加载到内存(后缀名不要加".") [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"美女" ofType:@"png"]]; NSLog(@"%@",readImage); NSLog(@"%@",[UIImage imageWithContentsOfFile:imageFilePath]); } #pragma mark - 单个--复杂的数据对象write/read-------NSCoding协议 - (void)complexOperationOfApp{ UIButton * archiver = [UIButton buttonWithType:UIButtonTypeSystem]; archiver.frame = CGRectMake(20, 100, 100, 40); [archiver setTitle:@"归档(序列化)" forState:UIControlStateNormal]; [archiver addTarget:self action:@selector(onClickArchiverButton:) forControlEvents:UIControlEventTouchUpInside]; [self.window addSubview:archiver]; UIButton * unArchiver = [UIButton buttonWithType:UIButtonTypeSystem]; unArchiver.frame = CGRectMake(160, 100, 120, 40); [unArchiver setTitle:@"反归档(反序列化)" forState:UIControlStateNormal]; [unArchiver addTarget:self action:@selector(onClickUnArchiverButton:) forControlEvents:UIControlEventTouchUpInside]; [self.window addSubview:unArchiver]; } #pragma mark 对复杂的数据对象进行写入操作.原理:复杂的数据对象序列化为NSData,将NSData写入文件,实现数据持久化 - (void)onClickArchiverButton:(UIButton *)button{ /** * 1.自定义复杂的数据类型,例如ABPerson * (1)ABPerson必须实现NSCoding协议的方法 * (2)ABPerson中的实例变量或属性,也必须实现NSCoding协议的方法 * (3)ABPerson中的基本数据类型没有过多限制 * 2.创建一个序列化工具对象 * 3.先找路径 * 4.对复杂的数据类型对象进行序列化 * 5.结束 finishEncoding * 6.NSData写入 */ HMTABPerson * person = [[HMTABPerson alloc]init]; person.name = @"HMT"; person.age = 25; NSMutableData * archiverData = [NSMutableData data]; NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:archiverData]; [archiver encodeObject:person forKey:@"PersonKey"]; [archiver finishEncoding]; NSString * documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; NSString * filePath = [documents stringByAppendingPathComponent:@"person.data"]; [archiverData writeToFile:filePath atomically:YES]; [person release]; [archiver release]; } #pragma mark 从文件中读取数据来实例化复杂对象数据操作 - (void)onClickUnArchiverButton:(UIButton *)button{ /** * 1.先找路径 * 2.NSData读出 * 3.创建一个反序列化工具 * 4.反序列化 * 5.结束 finishDecoding * 6.操作对象了 */ NSString * documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; NSString * filePath = [documents stringByAppendingPathComponent:@"person.data"]; NSData * unarchiverData = [NSData dataWithContentsOfFile:filePath]; NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:unarchiverData]; HMTABPerson * person = [unarchiver decodeObjectForKey:@"PersonKey"]; [unarchiver finishDecoding]; NSLog(@"%@",person.name); [unarchiver release]; } #pragma mark - 数组--复杂的数据对象write/read-------NSCoding协议 - (void)complexArrayOperationOfApp{ UIButton * archiver = [UIButton buttonWithType:UIButtonTypeSystem]; archiver.frame = CGRectMake(20, 200, 100, 40); [archiver setTitle:@"归档(序列化)" forState:UIControlStateNormal]; [archiver addTarget:self action:@selector(onClickArchiverArrayButton) forControlEvents:UIControlEventTouchUpInside]; [self.window addSubview:archiver]; UIButton * unArchiver = [UIButton buttonWithType:UIButtonTypeSystem]; unArchiver.frame = CGRectMake(160, 200, 120, 40); [unArchiver setTitle:@"反归档(反序列化)" forState:UIControlStateNormal]; [unArchiver addTarget:self action:@selector(onClickUnArchiverArrayButton) forControlEvents:UIControlEventTouchUpInside]; [self.window addSubview:unArchiver]; } #pragma mark 写入 - (void)onClickArchiverArrayButton{ NSMutableData * archiverData = [NSMutableData data]; NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:archiverData]; HMTABPerson * person1 = [[HMTABPerson alloc]init]; person1.name = @"陈凤长"; person1.age = 24; HMTABPerson * person2 = [[HMTABPerson alloc]init]; person2.name = @"胡明涛"; person2.age = 24; NSArray * personArray = @[person1,person2]; [archiver encodeObject:personArray forKey:@"PersonArrayKey"]; [archiver finishEncoding]; NSString * tmpPath = NSTemporaryDirectory(); NSString * filePath = [tmpPath stringByAppendingPathComponent:@"personArray.plist"]; [archiverData writeToFile:filePath atomically:YES]; [person1 release]; [person2 release]; [archiver release]; } #pragma mark 读取 - (void)onClickUnArchiverArrayButton{ NSString * tmpPath = NSTemporaryDirectory(); NSString * filePath = [tmpPath stringByAppendingPathComponent:@"personArray.plist"]; NSData * unarchiverData = [NSData dataWithContentsOfFile:filePath]; NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:unarchiverData]; NSArray * personArray = [unarchiver decodeObjectForKey:@"PersonArrayKey"]; [unarchiver finishDecoding]; HMTABPerson * person1 = [personArray objectAtIndex:0]; HMTABPerson * person2 = [personArray objectAtIndex:1]; NSLog(@"%@ %@",person1.name,person2.name); [unarchiver release]; } ~~~ HMTABPerson.h ~~~ #import @interface HMTABPerson : NSObject @property (nonatomic,assign)int age; @property (nonatomic,copy) NSString * name; @end ~~~ HMTABPerson.m ~~~ #import "HMTABPerson.h" @implementation HMTABPerson - (void)dealloc{ [_name release]; [super dealloc]; } // 序列化,编码 - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:_name forKey:@"NameKey"]; [aCoder encodeInt:_age forKey:@"AgeKey"]; } // 反序列化,解码 - (id)initWithCoder:(NSCoder *)aDecoder{ if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"NameKey"]; self.age = [aDecoder decodeIntForKey:@"AgeKey"]; } return self; } @end ~~~
';

NSBundle介绍以及读取沙盒文件路径问题

最后更新于:2022-04-01 22:58:38

@NSBundle包,是一种特定的文件类型,一个主要作用是 获取Resources文件夹中的资源      bundle是一个目录,其中包含了程序会使用到的资源. 这些资源包含了如图像,声音,编译好的代码,nib文件(用户也会把bundle称为plug-in). 对应bundle,cocoa提供了类NSBundle.            我们的程序是一个bundle. 在Finder中,一个应用程序看上去和其他文件没有什么区别. 但是实际上它是一个包含了nib文件,编译代码,以及其他资源的目录. 我们把这个目录叫做程序的main bundle。      [NSBundle mainBundle]是获得NSBundle的一个单例对象,次单例对象 已经设置了默认的resourcePath,也就是你的app打包后的路径,[NSBundle mainBundle]resourcePath]就是获得这个完整的打包后的app路径,但你的test.txt文件并不在这个目录,而是在app内部,这时就需要拼接路径字符串[NSString stringWithFormat:@"%@%@%@",[[NSBundle mainBundle]resourcePath],@"/",path]; ---------------------------------------------------------------------------------------------------------------------------------------------   iOS程序有固定的文件访问限制,只能在自己的沙盒内。   UIImage *img=[UIImage imageNamed:@"cellicon.png"];   这段代码从相对路径加载了一个png图片资源作为UIImage对象,没有任何问题。因为它在内部已经偷偷得帮你把路径转化为相对路径了,你还不知道吧!   接下来,如果要加载一些自定义格式的文件或者数据,就会出现相对路径和绝对路径的问题。比如   NSFileHandle *fileHandle=[NSFileHandlefile HandleForReadingAtPath:newPath];   newPath为一个字符串路径,这里的路径你却不能像上面那样   NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:@"test.txt"];   这样系统是找不到文件的,因为这里需要的是文件的绝对路径,而@“test.txt”完全是无效的。   这个时候就需要NSBundle的帮忙了,这个类专门用来负责路径转化等等功能那。大家都知道ios项目虽然在xcode下可以创建文件夹结构,但是实际上它是没有文件结构概念的,所以此时假如要加载放在项目目录下的test.txt文件,完整的代码应该是这样 方法1:   NSString *newPath=[NSString stringWithFormat:@"%@%@%@",[[NSBundle mainBundle]resourcePath],@"/",path];   NSLog(@"url=%@",newPath);   NSFileHandle *fileHandle=[NSFileHandlefile HandleForReadingAtPath:newPath]; 控制台输出app路径resourcePath: /Users/zouzouyanghong/Library/Application Support/iPhone Simulator/5.0/Applications/76116A6A-572D-43AE-AE78-17D7A88DCAFC/JFitnessProduct.app 控制台输出拼接后完整文件绝对路径: /Users/zouzouyanghong/Library/Application Support/iPhone Simulator/5.0/Applications/76116A6A-572D-43AE-AE78-17D7A88DCAFC/JFitnessProduct.app/test.jat 方法2:   NSString *newPath=[[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];   NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:newPath]; [[NSBundle mainBundle] pathForResource:@"test" ofType:@"jat"];前面的就不再介绍了,pathForResource方法有两个参数,前面这个为文件名,后面那个ofType就是文件类型,也就是文件后缀。 这里把文件名和后缀分开了,如果这是你得到的是一个完整的文件名如:@“test.txt”,那么你得自己想办法把文件名和后缀分开了。 @要特别说明,此处的文件后缀是不需要“.”的,如果写成@“.jat”是错误的,加载文件会失败。
';