iOS网络---NSURLSession

系列文章目录

iOS基础—Block
iOS基础—Protocol
iOS基础—KVC vs KVO
iOS网络—AFNetworking
iOS网络—NSURLSession
iOS内存管理—MRC vs ARC
iOS基础—Category vs Extension
iOS基础—多线程:GCD、NSThread、NSOperation
iOS基础—常用三方库:Masonry、SDWebImage
iOS基础—定时器:GCD、NSTimer、CADisplayLink



一、NSURLSession简介

在 Apple Developer 中查看 NSURLSession,发现它是位于 Foundation 层级下:

  • Foundation 是苹果公司提供的一个基本框架,用于支持 iOS、macOS、watchOS 和 tvOS 应用程序的开发。它提供了一系列基本的数据类型、集合、文件操作、日期和时间处理、网络通信等功能,是 Cocoa 和 Cocoa Touch 框架的核心部分。
  • NSURLSession 是 Foundation 框架中的一个类,专门用于处理网络请求和数据传输。它在 iOS 7 中引入,用于替代 NSURLConnection,提供了一种更现代和灵活的方式来进行网络通信。

在这里插入图片描述

二、NSURLSession架构

在这里插入图片描述

NSURLSession 这个名字,实际上是指代 Foundation 框架的 URL 加载系统中一些列相关的类和协议。上图所示为 NSURLSession 的系统架构图,主要由三个类构成:

  • NSURLSession
  • 负责请求/响应的关键对象,使用 NSURLSessionConfiguration 配置对象进行创建。
  • 在请求/响应的执行过程中调用 NSURLSessionTaskDelegate 所定义的各种代理方法。
  • NSURLSessionConfiguration
  • 用于对 NSURLSession 对象进行初始化,可以配置 可用网络、Cookie、安全性、缓存策略、自定义协议、启动事件 等选项,以及用于移动设备优化的相关选项。
  • 几乎可以配置任何选项。
  • NSURLSessionTask
  • 一个抽象类,其子类可以创建不同类型的任务(Task),如:下载、上传、获取数据(如:JSON 或 XML)。
  • 在特定 URL Session 中执行。

结合上述系统结构图,我们可以将 NSURLSession 中的类分为以下 6 种(如下图所示):

  1. URL 加载(URL Loading)
  2. 配置管理(Configuration Management)
  3. 缓存管理(Cache Policy)
  4. Cookie 存储(Cookie Storage)
  5. 认证和证书(Authentication and Credentials)
  6. 协议支持(Protocol Support)

在这里插入图片描述

在一个请求被发送到服务器之前,系统会先查询共享的缓存信息,然后根据 缓存策略(Cache Policy) 以及 可用性(availability) 的不同,一个已经被缓存的响应可能会被立即返回。如果没有缓存的响应可用,则这个请求将根据我们指定的策略来缓存它的响应,以便将来的请求可以使用。

在一个请求被发送到服务器过程中,服务器可能会发出 鉴权查询(Authorization Challenge),这可以由共享的 Cookie证书存储(Credential Storage) 来自动响应,或者由被委托对象来响应。此外,发送中的请求也可以被注册的 NSURLProtocol 对象所拦截,以便在必要时改变其加载行为。

下面我们依次来详细介绍 URL 加载系统中的 3 个主要类: NSURLSession、NSURLSessionTask、NSURLSessonConfiguration。在 NSURLSessionConfiguration 中,我们将对缓存策略、Cookie 存储、自定义协议等内容稍作介绍。

三、NSURLSessionTask

首先看看官方文档的定义:
在这里插入图片描述

通过NSURLSession发起的每个请求,都会被封装为一个NSURLSessionTask任务,但一般不会直接是NSURLSessionTask类,而是基于不同任务类型,被封装为其对应的子类。主要有三个子类:NSURLSessionDataTask、NSURLSessionDownloadTask 和 NSURLSessionUploadTask。这 3 个子类封装了 3 个最基本的网络任务:获取数据、下载文件、上传文件。

在这里插入图片描述

上图所示为这些类之间的继承关系。对于 NSURLSessionDataTask,服务器会有响应数据;而对于上传请求,服务器也会有响应数据,所以 NSURLSessionUploadTask 继承自 NSURLSessionDataTask。NSURLSessionDownloadTask 完成时,会带回已下载文件的一个临时的文件路径。

1.创建方式

NSURLSession 提供有普通创建 task 的方式,创建后可以通过重写代理方法,获取对应的回调和参数。这种方式对于请求过程比较好控制。

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

除此之外,NSURLSession 也提供了 block 的方式创建task,创建方式简单如AFN,直接传入 URL 或 NSURLRequest,即可直接在 block中接收返回数据。和普通创建方式一样,block 的创建方式创建后默认也是 suspend 的状态,需要调用 resume 开始任务。

completionHandler和delegate是互斥的,completionHandler的优先级大于delegate。相对于普通创建方法,block方式更偏向于面向结果的创建,可以直接在completionHandler中获取返回结果,但不能控制请求过程。

- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

2.获取方式

可以通过下面的两个方法,获取当前 session 对应的所有 task,方法区别在于回调的参数不同。下面是获取所有任务并打印任务信息的🌰:

//Asynchronously calls a completion callback with all data, upload, and download tasks in a session.
- (void)getTasksWithCompletionHandler:(void (^)(NSArray<NSURLSessionDataTask *> *dataTasks, 
                                                NSArray<NSURLSessionUploadTask *> *uploadTasks, 
                                                NSArray<NSURLSessionDownloadTask *> *downloadTasks))completionHandler;

//Asynchronously calls a completion callback with all tasks in a session
- (void)getAllTasksWithCompletionHandler:(void (^)(NSArray<__kindof NSURLSessionTask *> *tasks))completionHandler;

//获取所有任务并打印任务信息
//异步操作:getAllTasksWithCompletionHandler: 是异步操作,completion handler 会在所有任务被获取到后调用。
//线程安全:此方法是线程安全的,可以在任何线程上调用。
//任务类型:返回的任务数组包含所有类型的任务(数据任务、上传任务和下载任务)。
NSURLSession *session = [NSURLSession sharedSession];
[session getAllTasksWithCompletionHandler:^(NSArray<__kindof NSURLSessionTask *> *tasks) {
    for (NSURLSessionTask *task in tasks) {
        NSLog(@"Task: %@", task);
    }
}];

NSURLSession *session = [NSURLSession sharedSession];
[session getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> *dataTasks, NSArray<NSURLSessionUploadTask *> *uploadTasks, NSArray<NSURLSessionDownloadTask *> *downloadTasks) {
    NSLog(@"Data Tasks: %@", dataTasks);
    NSLog(@"Upload Tasks: %@", uploadTasks);
    NSLog(@"Download Tasks: %@", downloadTasks);
}];

3.数据返回方式

a.使用Completion Handler

Completion handler 是最常用的方式之一,适用于简单的请求和响应处理。你可以在创建任务时指定一个 completion handler,当任务完成时,系统会调用这个 handler 并传递响应数据。

下面是一个🌰:

NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                NSLog(@"Error: %@", error.localizedDescription);
            } else {
                // 打印原始数据字符串
                NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                NSLog(@"Raw Response: %@", dataString);

                // 尝试解析 JSON
                NSError *jsonError;
                NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                if (jsonError) {
                    NSLog(@"JSON Parsing Error: %@", jsonError.localizedDescription);
                } else {
                    NSLog(@"JSON Response: %@", json);
                }
            }
            // 任务完成,发送信号
            dispatch_semaphore_signal(semaphore);
        }];

b.使用 Delegate

使用 delegate 是另一种处理数据返回的方式,适用于需要更细粒度控制的情况。你可以实现 NSURLSessionDelegate、NSURLSessionTaskDelegate 和 NSURLSessionDataDelegate 协议的方法来处理任务的不同阶段。

我们即可以用系统提供的方法,以 NSURLSessionDataDelegate 为例:

//Tells the delegate that the data task received the initial reply (headers) from the server.
- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
didReceiveResponse:(NSURLResponse *)response 
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;

//Tells the delegate that the data task has received some of the expected data.
- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
    didReceiveData:(NSData *)data;

当然,我们也可以自己根据需求来重写方法:

@interface MySessionDelegate : NSObject <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSMutableData *receivedData;
@end

@implementation MySessionDelegate

- (instancetype)init {
    self = [super init];
    if (self) {
        _receivedData = [NSMutableData data];
    }
    return self;
}

// 接收到响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    NSLog(@"Received response: %@", response);
    [self.receivedData setLength:0]; // 清空数据
    completionHandler(NSURLSessionResponseAllow);
}

// 接收到数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
}

// 请求完成
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (error) {
        NSLog(@"Error: %@", error.localizedDescription);
    } else {
        NSString *dataString = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
        NSLog(@"Response Data: %@", dataString);
    }
}

@end

那什么时候需要我们自己实现呢?总结了下面几种情况:

  • 需要处理分段数据
    如果你的数据任务可能会接收到分段的数据,并且你需要将这些数据片段拼接成完整的数据,那么你需要实现这个方法。例如,下载大文件或接收流式数据时,数据可能会分段到达。
  • 需要处理数据流
    如果你需要处理数据流(例如,实时数据或长连接),你也需要实现这个方法,以便在数据到达时及时处理。

需要注意的是,NSURLSessionTask 及其子类都有着各自的代理协议,它们之间也存在着如下图所示的继承关系。

  • NSURLSessionDelegate:定义了网络请求最基础的代理方法。作为所有代理的基类。
  • NSURLSessionTaskDelegate:定义了网络请求任务相关的代理方法。
  • NSURLSessionDownloadDelegate:定义了下载任务相关的代理方法,如:下载进度等
  • NSURLSessionDataDelegate:定义了普通数据任务和上传任务相关的代理方法。

在这里插入图片描述

对于上传请求,服务器也会有响应数据,所以所用的代理是 NSURLSessionDataDelegate:
在这里插入图片描述

四、NSURLSession

首先看看官方文档的定义:
在这里插入图片描述

NSURLSession 是 iOS 和 macOS 开发中用于处理网络请求的类。它提供了一个 API,用于从网络中下载和上传数据。NSURLSession 支持多种任务类型,如数据任务、下载任务和上传任务,允许开发者进行异步网络通信。

1.创建 NSURLSession 对象

NSURLSession 对象是网络会话的核心。你可以使用共享会话 (sharedSession) 或自定义会话配置 (NSURLSessionConfiguration) 来创建会话对象。

创建方法有三种:

//1.系统维护的一个单例对象,可以和其他使用这个session的task共享连接和请求信息。
NSURLSession *session = [NSURLSession sharedSession];

//2.在NSURLSession初始化时传入一个NSURLSessionConfiguration,这样可以自定义请求头、cookie等信息。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;

//3.如果想更好的控制请求过程以及回调线程,需要上面的方法进行初始化操作,并传入delegate来设置回调对象和回调的线程。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration 
                                  delegate:(id<NSURLSessionDelegate>)delegate 
                             delegateQueue:(NSOperationQueue *)queue;

下面总结了一些创建 NSURLSession 对象的几种情况:

//1.通过共享的单例会话对象,适用于简单的网络请求,不需要自定义配置。
NSURLSession *session = [NSURLSession sharedSession];

//2.通过默认配置会话,适用于大多数网络请求场景,允许进行一些自定义配置。
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfig];

//3.通过临时配置会话,不使用持久存储(如缓存、cookie 等)。
NSURLSessionConfiguration *ephemeralConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralConfig];

//4.通过后台配置会话,适用于需要在应用程序挂起或终止时继续进行下载和上传任务的场景。
NSString *identifier = @"com.example.backgroundsession";
NSURLSessionConfiguration *backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];

2.创建任务

根据需要创建数据任务、下载任务或上传任务。这些任务可以使用 NSURLSession 对象的方法来创建,并且每个任务都有一个回调处理程序,用于处理响应数据。

通常可以创建四种任务:

//1.创建数据任务
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url 
                        completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request 
                            completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

//2.下载任务
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;

- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url 
                                completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request 
                                    completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData 
                                       completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

//3.上传任务
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request 
                                         fromData:(NSData *)bodyData;

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request 
                                         fromData:(NSData *)bodyData 
                                completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request 
                                         fromFile:(NSURL *)fileURL;

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request 
                                         fromFile:(NSURL *)fileURL 
                                completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

//4.流任务
- (NSURLSessionStreamTask *)streamTaskWithHostName:(NSString *)hostname 
                                              port:(NSInteger)port;

3.启动任务

在使用 NSURLSession 创建网络任务(如数据任务、下载任务和上传任务)后,需要显式调用 resume 方法来启动任务。这是因为 NSURLSession 设计为一种灵活且可控的网络通信方式,允许开发者在创建任务后进行进一步的配置和控制。

// 启动任务
[dataTask resume];

需要启动的原因:

  • 在创建任务后,开发者可能需要对任务进行一些额外的配置或操作,如设置请求头、添加任务级别的代理、配置超时等。通过显式调用 resume 方法,开发者可以在启动任务前完成这些操作。
  • NSURLSession 允许任务被暂停和恢复。通过显式调用 resume 方法,开发者可以在适当的时间点启动任务,并在需要时暂停或恢复任务。这对于需要处理网络状态变化或用户操作的应用程序非常有用。
  • 如果任务在创建后立即自动启动,可能会导致不必要的资源消耗。例如,在批量创建多个任务时,开发者可能希望在所有任务创建完成后再统一启动这些任务,以便更好地管理资源和任务调度。
  • 显式调用 resume 方法符合任务的生命周期管理,使得任务的创建、配置和启动步骤更加清晰和可控。这对于调试和维护代码也非常有帮助。

这里可以补充一下 NSURLSession 与 RunLoop 的关系:

  • NSURLSession 和 RunLoop 之间的关系在于任务的执行和回调的调度。RunLoop 是一个事件处理循环,它管理输入源(如触摸事件、网络事件)和定时器。NSURLSession 依赖于 RunLoop 来调度网络请求的回调和处理网络事件。
  • 当使用 NSURLSession 创建任务时,如果没有显式指定 delegate queue,NSURLSession 会默认使用当前线程的 RunLoop 来调度任务的回调。这意味着回调会在创建任务的线程上执行。
  • 开发者可以在创建 NSURLSession 时指定一个 NSOperationQueue 作为 delegate queue。这样,NSURLSession 会在指定的队列上调度回调,而不是依赖当前线程的 RunLoop。

4.处理响应

在任务的回调处理程序中处理响应数据,解析 JSON 或处理错误。

我们下面举一个完整的例子,我们访问API 测试端点,这个端点属于一个名为 JSONPlaceholder 的在线 REST API 服务,专门用于测试和原型设计。它返回的响应是 JSON 格式的数据。然后我们用 NSJSONSerialization 来解析 JSON 格式的数据。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建信号量
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        // 创建 NSURLSession 对象
        NSURLSession *session = [NSURLSession sharedSession];
        NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/todos/1"];

        // 创建 NSURLSessionDataTask 任务
        NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                NSLog(@"Error: %@", error.localizedDescription);
            } else {
                // 打印原始数据字符串
                NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                NSLog(@"Raw Response: %@", dataString);

                // 尝试解析 JSON
                NSError *jsonError;
                NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                if (jsonError) {
                    NSLog(@"JSON Parsing Error: %@", jsonError.localizedDescription);
                } else {
                    NSLog(@"JSON Response: %@", json);
                }
            }
            // 任务完成,发送信号
            dispatch_semaphore_signal(semaphore);
        }];

        // 启动任务
        [dataTask resume];

        // 等待任务完成
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
    return 0;
}

运行的结果如下:
在这里插入图片描述

五、NSURLSessionConfiguration

首先看看官方文档的定义:
在这里插入图片描述

NSURLSessionConfiguration 是一个配置对象,用于定义 NSURLSession 实例的行为和策略。通过配置 NSURLSessionConfiguration,开发者可以控制会话的缓存策略、cookie 处理、连接超时、HTTP 请求头等。

1.创建 NSURLSessionConfiguration

NSURLSessionConfiguration 提供了三种预定义的配置方式:

  1. 默认配置 (defaultSessionConfiguration)
  2. 临时配置 (ephemeralSessionConfiguration)
  3. 后台配置 (backgroundSessionConfigurationWithIdentifier:)
//默认配置使用系统默认的缓存、cookie 和证书策略,适用于大多数网络请求场景。
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];

//临时配置不使用持久存储(如缓存、cookie 等),适用于需要临时会话的场景。
NSURLSessionConfiguration *ephemeralConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];

//后台配置允许在应用程序挂起或终止时继续进行下载和上传任务。需要提供一个唯一的标识符来创建后台会话。
NSString *identifier = @"com.example.backgroundsession";
NSURLSessionConfiguration *backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];

2.属性配置

NSURLSessionConfiguration 拥有数十个配置属性。熟练掌握这些配置属性的用处,可以让应用程序充分地利用其网络环境。

这里贴一个快速访问的🔗 :NSURLSessionConfiguration

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS开发中上传图片可以采用以下步骤: 1.选择要上传的图片,可以使用系统提供的UIImagePickerController控制器,或者使用第三方库,例如TZImagePickerController。 2.将选中的图片转换为NSData格式。 3.使用NSURLSession或AFNetworking等网络库,将图片数据上传到服务器。 以下是一个简单的上传图片的示例代码: ``` // 选择图片 UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; // 将选中的图片转换为NSData格式 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info { UIImage *selectedImage = info[UIImagePickerControllerOriginalImage]; NSData *imageData = UIImageJPEGRepresentation(selectedImage, 0.5); // 上传图片到服务器 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; NSURL *url = [NSURL URLWithString:@"http://example.com/upload.php"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理服务器返回的响应 }]; [uploadTask resume]; [picker dismissViewControllerAnimated:YES completion:nil]; } ``` 其中,upload.php是服务器端接收图片的脚本文件。在服务器端,可以使用PHP等语言来处理上传的图片数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值