文档章节

iOS网络 NSURLSession 转载

沉恋
 沉恋
发布于 2015/02/22 14:31
字数 3108
阅读 103
收藏 3

NSURLConnection是2003年伴随着Safari一起发行的网络开发API,距今已经有十一年。当然,在这十一年间它表现的相当优秀,有大量的应用基础,这也是为什么前面花了那么长时间对它进行详细介绍的原因。但是这些年伴随着iPhone、iPad的发展,对于NSURLConnection设计理念也提出了新的挑战。在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者。相比较NSURLConnection,NSURLSession提供了配置会话缓存、协议、cookie和证书能力,这使得网络架构和应用程序可以独立工作、互不干扰。另外,NSURLSession另一个重要的部分是会话任务,它负责加载数据,在客户端和服务器端进行文件的上传下载。

NSURLSession

通过前面的介绍大家可以看到,NSURLConnection完成的三个主要任务:获取数据(通常是JSON、XML等)、文件上传、文件下载。其实在NSURLSession时代,他们分别由三个任务来完成:NSURLSessionData、NSURLSessionUploadTask、NSURLSessionDownloadTask,这三个类都是NSURLSessionTask这个抽象类的子类,相比直接使用NSURLConnection,NSURLSessionTask支持任务的暂停、取消和恢复,并且默认任务运行在其他非主线程中,具体关系图如下:

 NSURLSession_Class

数据请求

前面通过请求一个微博数据进行数据请求演示,现在通过NSURLSessionDataTask实现这个功能,其实现流程与使用NSURLConnection的静态方法类似,下面是主要代码:

-(void)loadJsonData{
    //1.创建url
    NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.208/ViewStatus.aspx?userName=%@&password=%@",@"KenshinCui",@"123"];
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //2.创建请求
    NSURLRequest *request=[NSURLRequest requestWithURL:url];
    
    //3.创建会话(这里使用了一个全局会话)并且启动任务
    NSURLSession *session=[NSURLSession sharedSession];
    //从会话创建任务
    NSURLSessionDataTask *dataTask=[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {
            NSString *dataStr=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@",dataStr);
        }else{
            NSLog(@"error is :%@",error.localizedDescription);
        }
    }];
    
    [dataTask resume];//恢复线程,启动任务
}



文件上传

下面看一下如何使用NSURLSessionUploadTask实现文件上传,这里贴出主要的几个方法:

#pragma mark 取得mime types
-(NSString *)getMIMETypes:(NSString *)fileName{
    return @"image/jpg";
}
#pragma mark 取得数据体
-(NSData *)getHttpBody:(NSString *)fileName{
    NSString *boundary=@"KenshinCui";
    NSMutableData *dataM=[NSMutableData data];
    NSString *strTop=[NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"file1\"; filename=\"%@\"\nContent-Type: %@\n\n",boundary,fileName,[self getMIMETypes:fileName]];
    NSString *strBottom=[NSString stringWithFormat:@"\n--%@--",boundary];
    NSString *filePath=[[NSBundle mainBundle] pathForResource:fileName ofType:nil];
    NSData *fileData=[NSData dataWithContentsOfFile:filePath];
    [dataM appendData:[strTop dataUsingEncoding:NSUTF8StringEncoding]];
    [dataM appendData:fileData];
    [dataM appendData:[strBottom dataUsingEncoding:NSUTF8StringEncoding]];
    return dataM;
}
#pragma mark 上传文件
-(void)uploadFile{
    NSString *fileName=@"pic.jpg";
    //1.创建url
    NSString *urlStr=@"http://192.168.1.208/FileUpload.aspx";
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //2.创建请求
    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod=@"POST";
    
    //3.构建数据
    NSString *path=[[NSBundle mainBundle] pathForResource:fileName ofType:nil];
    NSData *data=[self getHttpBody:fileName];
    request.HTTPBody=data;
    
    [request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)data.length] forHTTPHeaderField:@"Content-Length"];
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",@"KenshinCui"] forHTTPHeaderField:@"Content-Type"];
    
    

    //4.创建会话
    NSURLSession *session=[NSURLSession sharedSession];
    NSURLSessionUploadTask *uploadTask=[session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {
            NSString *dataStr=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@",dataStr);
        }else{
            NSLog(@"error is :%@",error.localizedDescription);
        }
    }];
    
    [uploadTask resume];
}

如果仅仅通过上面的方法或许文件上传还看不出和NSURLConnection之间的区别,因为拼接上传数据的过程和前面是一样的。事实上在NSURLSessionUploadTask中还提供了一个- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler方法用于文件上传。这个方法通常会配合“PUT”请求进行使用,由于PUT方法包含在Web DAV协议中,不同的WEB服务器其配置启用PUT的方法也不同,并且出于安全考虑,各类WEB服务器默认对PUT请求也是拒绝的,所以实际使用时还需做重分考虑,在这里不具体介绍,有兴趣的朋友可以自己试验一下。

文件下载

使用NSURLSessionDownloadTask下载文件的过程与前面差不多,需要注意的是文件下载文件之后会自动保存到一个临时目录,需要开发人员自己将此文件重新放到其他指定的目录中。

-(void)downloadFile{
    //1.创建url
    NSString *fileName=@"1.jpg";
    NSString *urlStr=[NSString stringWithFormat: @"http://192.168.1.208/FileDownload.aspx?file=%@",fileName];
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //2.创建请求
    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
    
    //3.创建会话(这里使用了一个全局会话)并且启动任务
    NSURLSession *session=[NSURLSession sharedSession];
    
    NSURLSessionDownloadTask *downloadTask=[session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        if (!error) {
            //注意location是下载后的临时保存路径,需要将它移动到需要保存的位置
            
            NSError *saveError;
            NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
            NSString *savePath=[cachePath stringByAppendingPathComponent:fileName];
            NSLog(@"%@",savePath);
            NSURL *saveUrl=[NSURL fileURLWithPath:savePath];
            [[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&saveError];
            if (!saveError) {
                NSLog(@"save sucess.");
            }else{
                NSLog(@"error is :%@",saveError.localizedDescription);
            }
            
        }else{
            NSLog(@"error is :%@",error.localizedDescription);
        }
    }];
    
    [downloadTask resume];
}



会话

NSURLConnection通过全局状态来管理cookies、认证信息等公共资源,这样如果遇到两个连接需要使用不同的资源配置情况时就无法解决了,但是这个问题在NSURLSession中得到了解决。NSURLSession同时对应着多个连接,会话通过工厂方法来创建,同一个会话中使用相同的状态信息。NSURLSession支持进程三种会话:

  1. defaultSessionConfiguration:进程内会话(默认会话),用硬盘来缓存数据。 
  2. ephemeralSessionConfiguration:临时的进程内会话(内存),不会将cookie、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。 
  3. backgroundSessionConfiguration:后台会话,相比默认会话,该会话会在后台开启一个线程进行网络数据处理。

下面将通过一个文件下载功能对两种会话进行演示,在这个过程中也会用到任务的代理方法对上传操作进行更加细致的控制。下面先看一下使用默认会话下载文件,代码中演示了如何通过NSURLSessionConfiguration进行会话配置,如果通过代理方法进行文件下载进度展示(类似于前面中使用NSURLConnection代理方法,其实下载并未分段,如果需要分段需要配合后台进行),同时在这个过程中可以准确控制任务的取消、挂起和恢复。

//
//  KCMainViewController.m
//  URLSession
//
//  Created by Kenshin Cui on 14-03-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"

@interface KCMainViewController ()<NSURLSessionDownloadDelegate>{
    UITextField *_textField;
    UIProgressView *_progressView;
    UILabel *_label;
    UIButton *_btnDownload;
    UIButton *_btnCancel;
    UIButton *_btnSuspend;
    UIButton *_btnResume;
    NSURLSessionDownloadTask *_downloadTask;
}

@end

@implementation KCMainViewController

#pragma mark - UI方法
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setUi];
    
}

#pragma mark 界面布局
-(void)setUi{ 
    //地址栏
    _textField=[[UITextField alloc]initWithFrame:CGRectMake(10, 50, 300, 25)];
    _textField.borderStyle=UITextBorderStyleRoundedRect;
    _textField.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0];
    _textField.text=@"[Objective-C.程序设计(第4版)].(斯蒂芬).林冀等.扫描版[电子书www.minxue.net].pdf";
    [self.view addSubview:_textField];
    //进度条
    _progressView=[[UIProgressView alloc]initWithFrame:CGRectMake(10, 100, 300, 25)];
    [self.view addSubview:_progressView];
    //状态显示
    _label=[[UILabel alloc]initWithFrame:CGRectMake(10, 130, 300, 25)];
    _label.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0];
    [self.view addSubview:_label];
    //下载按钮
    _btnDownload=[[UIButton alloc]initWithFrame:CGRectMake(20, 500, 50, 25)];
    [_btnDownload setTitle:@"下载" forState:UIControlStateNormal];
    [_btnDownload setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
    [_btnDownload addTarget:self action:@selector(downloadFile) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btnDownload];
    //取消按钮
    _btnCancel=[[UIButton alloc]initWithFrame:CGRectMake(100, 500, 50, 25)];
    [_btnCancel setTitle:@"取消" forState:UIControlStateNormal];
    [_btnCancel setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
    [_btnCancel addTarget:self action:@selector(cancelDownload) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btnCancel];
    //挂起按钮
    _btnSuspend=[[UIButton alloc]initWithFrame:CGRectMake(180, 500, 50, 25)];
    [_btnSuspend setTitle:@"挂起" forState:UIControlStateNormal];
    [_btnSuspend setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
    [_btnSuspend addTarget:self action:@selector(suspendDownload) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btnSuspend];
    //恢复按钮
    _btnResume=[[UIButton alloc]initWithFrame:CGRectMake(260, 500, 50, 25)];
    [_btnResume setTitle:@"恢复" forState:UIControlStateNormal];
    [_btnResume setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
    [_btnResume addTarget:self action:@selector(resumeDownload) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btnResume];
}
#pragma mark 设置界面状态
-(void)setUIStatus:(int64_t)totalBytesWritten expectedToWrite:(int64_t)totalBytesExpectedToWrite{
    dispatch_async(dispatch_get_main_queue(), ^{
        _progressView.progress=(float)totalBytesWritten/totalBytesExpectedToWrite;
        if (totalBytesWritten==totalBytesExpectedToWrite) {
            _label.text=@"下载完成";
            [UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
            _btnDownload.enabled=YES;
        }else{
            _label.text=@"正在下载...";
            [UIApplication sharedApplication].networkActivityIndicatorVisible=YES;
        }
    });
}


#pragma mark 文件下载
-(void)downloadFile{
    //1.创建url
    NSString *fileName=_textField.text;
    NSString *urlStr=[NSString stringWithFormat: @"http://192.168.1.208/FileDownload.aspx?file=%@",fileName];
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //2.创建请求
    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
    
    //3.创建会话
    //默认会话
    NSURLSessionConfiguration *sessionConfig=[NSURLSessionConfiguration defaultSessionConfiguration];
    sessionConfig.timeoutIntervalForRequest=5.0f;//请求超时时间
    sessionConfig.allowsCellularAccess=true;//是否允许蜂窝网络下载(2G/3G/4G)
    //创建会话
    NSURLSession *session=[NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];//指定配置和代理
    _downloadTask=[session downloadTaskWithRequest:request];

    [_downloadTask resume];
}
#pragma mark 取消下载
-(void)cancelDownload{
    [_downloadTask cancel];
    
}
#pragma mark 挂起下载
-(void)suspendDownload{
    [_downloadTask suspend];
}
#pragma mark 恢复下载下载
-(void)resumeDownload{
    [_downloadTask resume];
}

#pragma mark - 下载任务代理
#pragma mark 下载中(会多次调用,可以记录下载进度)
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    [self setUIStatus:totalBytesWritten expectedToWrite:totalBytesExpectedToWrite];
}

#pragma mark 下载完成
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    NSError *error;
    NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *savePath=[cachePath stringByAppendingPathComponent:_textField.text];
    NSLog(@"%@",savePath);
    NSURL *saveUrl=[NSURL fileURLWithPath:savePath];
    [[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&error];
    if (error) {
        NSLog(@"Error is:%@",error.localizedDescription);
    }
}

#pragma mark 任务完成,不管是否下载成功
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    [self setUIStatus:0 expectedToWrite:0];
    if (error) {
        NSLog(@"Error is:%@",error.localizedDescription);
    }
}
@end



演示效果:

NSURLSession_FileDownLoad

NSURLSession支持程序的后台下载和上传,苹果官方将其称为进程之外的上传和下载,这些任务都是交给后台守护线程完成的,而非应用程序本身。即使文件在下载和上传过程中崩溃了也可以继续运行(注意如果用户强制退关闭应用程序,NSURLSession会断开连接)。下面看一下如何在后台进行文件下载,这在实际开发中往往很有效,例如在手机上缓存一个视频在没有网络的时候观看(为了简化程序这里不再演示任务的取消、挂起等操作)。下面对前面的程序稍作调整使程序能在后台完成下载操作:

//
//  KCMainViewController.m
//  URLSession
//
//  Created by Kenshin Cui on 14-03-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import "AppDelegate.h"

@interface KCMainViewController ()<NSURLSessionDownloadDelegate>{
    NSURLSessionDownloadTask *_downloadTask;
    NSString *_fileName;
}

@end

@implementation KCMainViewController

#pragma mark - UI方法
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self downloadFile];
}

#pragma mark 取得一个后台会话(保证一个后台会话,这通常很有必要)
-(NSURLSession *)backgroundSession{
    static NSURLSession *session;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        NSURLSessionConfiguration *sessionConfig=[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.cmjstudio.URLSession"];
        sessionConfig.timeoutIntervalForRequest=5.0f;//请求超时时间
        sessionConfig.discretionary=YES;//系统自动选择最佳网络下载
        sessionConfig.HTTPMaximumConnectionsPerHost=5;//限制每次最多一个连接
        //创建会话
        session=[NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];//指定配置和代理
    });
    return session;
}

#pragma mark 文件下载
-(void)downloadFile{
    _fileName=@"1.mp4";
    NSString *urlStr=[NSString stringWithFormat: @"http://192.168.1.208/FileDownload.aspx?file=%@",_fileName];
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
    
    //后台会话
    _downloadTask=[[self backgroundSession] downloadTaskWithRequest:request];
    
    [_downloadTask resume];
}
#pragma mark - 下载任务代理
#pragma mark 下载中(会多次调用,可以记录下载进度)
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
//    [NSThread sleepForTimeInterval:0.5];
//    NSLog(@"%.2f",(double)totalBytesWritten/totalBytesExpectedToWrite);
}

#pragma mark 下载完成
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    NSError *error;
    NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *savePath=[cachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@",[NSDate date]]];
    NSLog(@"%@",savePath);
    NSURL *saveUrl=[NSURL fileURLWithPath:savePath];
    [[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&error];
    if (error) {
        NSLog(@"didFinishDownloadingToURL:Error is %@",error.localizedDescription);
    }
}

#pragma mark 任务完成,不管是否下载成功
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    if (error) {
        NSLog(@"DidCompleteWithError:Error is %@",error.localizedDescription);
    }
}
@end



运行上面的程序会发现即使程序退出到后台也能正常完成文件下载。为了提高用户体验,通常会在下载时设置文件下载进度,但是通过前面的介绍可以知道:当程序进入后台后,事实上任务是交给iOS系统来调度的,具体什么时候下载完成就不得而知,例如有个较大的文件经过一个小时下载完了,正常打开应用程序看到的此文件下载进度应该在100%的位置,但是由于程序已经在后台无法更新程序UI,而此时可以通过应用程序代理方法进行UI更新。具体原理如下图所示:

transfer

当NSURLSession在后台开启几个任务之后,如果有其中几个任务完成后系统会调用此应用程序的-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler代理方法;此方法会包含一个competionHandler(此操作表示应用完成所有处理工作),通常我们会保存此对象;直到最后一个任务完成,此时会重新通过会话标识(上面sessionConfig中设置的)找到对应的会话并调用NSURLSession的-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session代理方法,在这个方法中通常可以进行UI更新,并调用completionHandler通知系统已经完成所有操作。具体两个方法代码示例如下:

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
    
    //backgroundSessionCompletionHandler是自定义的一个属性
    self.backgroundSessionCompletionHandler=completionHandler;
   
}

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
    //Other Operation....
    
    if (appDelegate.backgroundSessionCompletionHandler) {
        
        void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
        
        appDelegate.backgroundSessionCompletionHandler = nil;
        
        completionHandler();
        
    }
}




本文转载自:http://www.cnblogs.com/kenshincui/p/4042190.html#NSURLSession

共有 人打赏支持
沉恋
粉丝 9
博文 148
码字总数 63031
作品 0
苏州
私信 提问
iOS基础深入补完计划--NSURLSession使用详解(附Demo)

目录 前言 API Demo 前言 本文主要是把NSURLSession以及NSURLSessionTask相关的代理方法全部整理了一下。 旨在大体了解在一个iOS网络请求中、一个任务究竟经理了什么。 而我们、又能做些什么...

kirito_song
05/10
0
0
iOS源码补完计划--AFNetworking 3.1.0源码研读

参拜一下AFNetworking的源码。 第四篇源码、暂时来看也是iOS方向的最后一篇、撸完准备趁着热乎撸一撸网络协议。 目录 准备工作 功能模块 AFURLSessionManager/AFHTTPSessionManager AFNetwo...

kirito_song
05/25
0
0
AFNetworking是一个为 iOS 和 Mac OSX 制作的令人愉快的网络库

AFNetworking是一个为 iOS 和 Mac OSX 制作的令人愉快的网络库,它建立在URL 装载系统框架的顶层,内置在Cocoa里,扩展了强有力的高级网络抽象。它的模块架构被良好的设计,拥有丰富的功能,...

lewis-180
2015/04/02
0
0
iOS源码补完计划--AFNetworking(一)

目录 前言 AFURLSessionManager AFHTTPSessionManager API注释Demo 参考 前言 AFNetworking第一篇 主要看了看AFURLSessionManager以及AFHTTPSessionManager相关的API AFN概述:《iOS源码补完计...

kirito_song
05/15
0
0
那些在学习iOS开发前就应该知道的事(part 2)

英文原文:Things I wish I had known before starting iOS development—Part 2 如果你还没读这篇文章的第一部分,请先读完了再来看第二部分。 那些在学习iOS开发前就应该知道的事(part 1)...

TomatosX
2015/06/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Kubernetes里的secret最基本的用法

Secret解决了密码、token、密钥等敏感数据的配置问题,使用Secret可以避免把这些敏感数据以明文的形式暴露到镜像或者Pod Spec中。 Secret可以以Volume或者环境变量的方式使用。 使用如下命令...

JerryWang_SAP
昨天
1
0
可重入锁和非可重入锁

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。 可重入锁: ReentrantLoc...

狼王黄师傅
昨天
1
0
2018-11-20学习笔记

1. python数据类型: 给变量赋值什么样的值,变量就是什么样的类型 给变量赋值整数,变量就是整数类型 给变量赋值字符串,变量就是字符串类型 123 和“123”一样吗? 在python中 单引号 与双...

laoba
昨天
1
0
使用 React 和 Vue 创建相同的应用,他们有什么差异?

在工作中应用 Vue 之后,我对它有了相当深刻的理解。 不过,俗话说「外国的月亮比较圆」,我好奇「外国的」 React 是怎么样的。 我阅读了 React 文档并观看了一些教程视频,虽然它们很棒,但...

阿K1225
昨天
2
0
2天闭门培训|以太坊智能合约从入门到实战(北京)

2天培训 16个课时 探寻技术原理,精通以太坊智能合约开发 以太坊智能合约是现在应用的最广泛的区块链应用开发方式,HiBlock区块链社区针对以太坊智能合约的学习特别推出2天闭门研修班,通过2...

HiBlock
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部