Alamofire框架的使用一 —— 基本用法

对于使用Objective-C的开发者,一定非常熟悉AFNetworking这个网络框架。在苹果推出的Swift之后,AFNetworking的作者专门用Swift来编写一个类似AFNetworking的网络框架,称为AlamofireAlamofire地址 >>

我分两篇文章介绍如何使用Alamofire框架。文章的内容主要是翻译Alamofire的readme。第二篇文章 >>

功能

  • 链式请求/响应方法
  • URL / JSON / plist参数编码
  • 上传文件/数据/流/多表单数据
  • 使用请求或者断点下载来下载文件
  • 使用URL凭据进行身份认证
  • HTTP响应验证
  • 包含进度的上传和下载闭包
  • cURL命令的输出
  • 动态适配和重试请求
  • TLS证书和Public Key Pinning
  • 网络可达性
  • 全面的单元和集成测试覆盖率

组件库

为了让Alamofire专注于核心网络的实现,Alamofire生态系统还有另外两个库:

  • AlamofireImage:一个图片库,包括图像响应序列化器、UIImageUIImageView的扩展、自定义图像滤镜、内存中自动清除和基于优先级的图像下载系统。
  • AlamofireNetworkActivityIndicator:控制iOS应用的网络活动指示器。包含可配置的延迟计时器来帮助减少闪光,并且支持不受Alamofire管理的URLSession实例。

要求的使用环境

  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.3+
  • Swift 3.1+

安装方法

CocoaPods

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks! target '项目名称' do
pod 'Alamofire', '~> 4.7'
end

iOS版本和Alamofire版本可以自己根据实际情况自行更改。CocoaPods是比较常用的第三方库管理工具,其他方法就不详细说了。

如何使用

发请求

Alamofire.request("http://baidu.com/")

响应处理

直接在请求后面用点语法链接响应处理:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.request) // 原始的URL请求
print(response.response) // HTTP URL响应
print(response.data) // 服务器返回的数据
print(response.result) // 响应序列化结果,在这个闭包里,存储的是JSON数据 if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}

在上面的例子中,responseJSON handler直接拼接到请求后面,当请求完成后被调用。这个闭包一旦收到响应后,就会处理这个响应,并不会因为等待服务器的响应而造成阻塞执行。请求的结果仅在响应闭包的范围内可用。其他任何与服务器返回的响应或者数据相关的操作,都必须在这个闭包内执行。

Alamofire默认情况下包含五种不同的响应handler:

// 响应 Handler - 未序列化的响应
func response(
queue: DispatchQueue?,
completionHandler: @escaping (DefaultDataResponse) -> Void)
-> Self // 响应数据 Handler - 序列化成数据类型
func responseData(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Data>) -> Void)
-> Self // 响应字符串 Handler - 序列化成字符串类型
func responseString(
queue: DispatchQueue?,
encoding: String.Encoding?,
completionHandler: @escaping (DataResponse<String>) -> Void)
-> Self // 响应 JSON Handler - 序列化成Any类型
func responseJSON(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self // 响应 PropertyList (plist) Handler - 序列化成Any类型
func responsePropertyList(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void))
-> Self

所有的响应handler都不会对响应进行验证。也就是说响应状态码在400..<500500..<600范围内,都不会触发错误。

响应 Handler

response handler不处理任何响应数据。它仅仅是从URL session delegate中转发信息。

Alamofire.request("https://httpbin.org/get").response { response in
print("Request: \(response.request)")
print("Response: \(response.response)")
print("Error: \(response.error)") if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}

一般情况下不建议使用这种没有响应序列化器的handler,而应该使用下面有特定序列化器的handler。

响应数据 Handler

responseData handler使用responseDataSerializer(这个对象把服务器的数据序列化成其他类型)来提取服务器返回的数据。如果没有返回错误并且有数据返回,那么响应Result将会是.successvalueData类型。

Alamofire.request("https://httpbin.org/get").responseData { response in
debugPrint("All Response Info: \(response)") if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
响应字符串 Handler

responseString handler使用responseStringSerializer对象根据指定的编码格式把服务器返回的数据转换成String。如果没有返回错误并且服务器的数据成功地转换为String,那么响应Result将会是.successvalueString类型。

Alamofire.request("https://httpbin.org/get").responseString { response in
print("Success: \(response.result.isSuccess)")
print("Response String: \(response.result.value)")
}

如果没有指定编码格式,将会使用服务器的HTTPURLResponse指定的格式。如果服务器无法确定编码格式,那么默认使用.isoLatin1

响应 JSON Handler

responseJSON handler使用responseJSONSerializer根据指定的JSONSerialization.ReadingOptions把服务器返回的数据转换成Any类型。如果没有返回错误并且服务器的数据成功地转换为JSON对象,那么响应Result将会是.successvalueAny类型。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
debugPrint(response) if let json = response.result.value {
print("JSON: \(json)")
}
}

所有JSON的序列化,都是使用JSONSerialization完成的。

链式响应handler

响应handler可以链接在一起:

Alamofire.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
}

注意:在同一个请求中使用多个响应handler,要求服务器的数据会被序列化多次,每次对应一个handler。

响应handler队列

默认情况下,响应handler是在主队列执行的。但是我们也可以自定义队列:

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
print("Executing response handler on utility queue")
}

响应验证

默认情况下,Alamofire把所有完成的请求当做是成功的请求,无论响应的内容是什么。如果响应有一个不能被接受的状态码或者MIME类型,在响应handler之前调用validate将会产生错误。

手动验证
Alamofire.request("https://httpbin.org/get")
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseData { response in
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print(error)
}
}
自动验证

自动验证在200…299范围内的状态码;如果请求头中有指定Accept,那么也会验证响应头的与请求头Accept一样的Content-Type

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print(error)
}
}

响应缓存

响应缓存是使用系统的框架URLCache来处理的。它提供了内存和磁盘上的缓存,并允许我们控制内存和磁盘的大小。

默认情况下,Alamofire利用共享的URLCache

HTTP方法

HTTPMethod列举了下面的这些方法:

public enum HTTPMethod: String {
case options = "OPTIONS"
case get = "GET"
case head = "HEAD"
case post = "POST"
case put = "PUT"
case patch = "PATCH"
case delete = "DELETE"
case trace = "TRACE"
case connect = "CONNECT"
}

在使用Alamofire.request时,可以传入方法参数:

Alamofire.request("https://httpbin.org/get") // 默认是get请求

Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)

参数编码

Alamofire支持三种参数编码:URLJSONPropertyList。还支持遵循了ParameterEncoding协议的自定义编码。

URL编码

URLEncoding类型创建了一个URL编码的查询字符串来设置或者添加到一个现有的URL查询字符串,或者设置URL请求的请求体。查询字符串是否被设置或者添加到现有的URL查询字符串,或者被作为HTTP请求体,决定于编码的Destination。编码的Destination有三个case:

  • .methodDependent:为GETHEADDELETE请求使用编码查询字符串来设置或者添加到现有查询字符串,并且使用其他HTTP方法来设置请求体。
  • .queryString:设置或者添加编码查询字符串到现有查询字符串
  • .httpBody:把编码查询字符串作为URL请求的请求体

一个编码请求的请求体的Content-Type字段被设置为application/x-www-form-urlencoded; charset=utf-8。因为没有公开的标准说明如何编码集合类型,所以按照惯例在key后面添加[]来表示数组的值(foo[]=1&foo[]=2),在key外面包一个中括号来表示字典的值(foo[bar]=baz)。

使用URL编码参数的GET请求
let parameters: Parameters = ["foo": "bar"]

// 下面这三种写法是等价的
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding 默认是`URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent)) // https://httpbin.org/get?foo=bar
使用URL编码参数的POST请求
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
] // 下面这三种写法是等价的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody) // HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
设置Bool类型参数的编码

URLEncoding.BoolEncoding提供了两种编码方式:

  • .numeric:把true编码为1false编码为0
  • .literal:把true编码为truefalse编码为false

默认情况下:Alamofire使用.numeric

可以使用下面的初始化函数来创建URLEncoding,指定Bool编码的类型:

let encoding = URLEncoding(boolEncoding: .literal)
设置Array类型参数编码

URLEncoding.ArrayEncoding提供了两种编码方式:

  • .brackets: 在每个元素值的key后面加上一个[],如foo=[1,2]编码成foo[]=1&foo[]=2
  • .noBrackets:不添加[],例如foo=[1,2]编码成``foo=1&foo=2`

默认情况下,Alamofire使用.brackets

可以使用下面的初始化函数来创建URLEncoding,指定Array编码的类型:

let encoding = URLEncoding(arrayEncoding: .noBrackets)
JSON编码

JSONEncoding类型创建了一个JOSN对象,并作为请求体。编码请求的请求头的Content-Type请求字段被设置为application/json

使用JSON编码参数的POST请求
let parameters: Parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
] // 下面这两种写法是等价的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: [])) // HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
属性列表编码

PropertyListEncoding根据关联格式和写选项值,使用PropertyListSerialization来创建一个属性列表对象,并作为请求体。编码请求的请求头的Content-Type请求字段被设置为application/x-plist

自定义编码

如果提供的ParameterEncoding类型不能满足我们的要求,可以创建自定义编码。下面演示如何快速自定义一个JSONStringArrayEncoding类型把JSON字符串数组编码到请求中。

struct JSONStringArrayEncoding: ParameterEncoding {
private let array: [String] init(array: [String]) {
self.array = array
} func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = urlRequest.urlRequest let data = try JSONSerialization.data(withJSONObject: array, options: []) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
} urlRequest.httpBody = data return urlRequest
}
}
手动URL请求参数编码

ParameterEncodingAPI可以在创建网络请求外面使用。

let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url) let parameters: Parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters)

HTTP请求头

可以直接在请求方法添加自定义HTTP请求头,这有利于我们在请求中添加请求头。

let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
] Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}

对于那些不变的请求头,建议在URLSessionConfiguration设置,这样就可以自动被用于任何URLSession创建的URLSessionTask

默认的Alamofire SessionManager为每一个请求提供了一个默认的请求头集合,包括:

  • Accept-Encoding,默认是gzip;q=1.0, compress;q=0.5
  • Accept-Language,默认是系统的前6个偏好语言,格式类似于en;q=1.0
  • User-Agent,包含当前应用程序的版本信息。例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0

如果要自定义这些请求头集合,我们必须创建一个自定义的URLSessionConfigurationdefaultHTTPHeaders属性将会被更新,并且自定义的会话配置也会应用到新的SessionManager实例。

认证

认证是使用系统框架URLCredentialURLAuthenticationChallenge实现的。

支持的认证方案
  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM
HTTP Basic认证

在合适的时候,在一个请求的authenticate方法会自动提供一个URLCredentialURLAuthenticationChallenge

let user = "user"
let password = "password" Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(user: user, password: password)
.responseJSON { response in
debugPrint(response)
}

根据服务器实现,Authorization header也可能是适合的:

let user = "user"
let password = "password" var headers: HTTPHeaders = [:] if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
headers[authorizationHeader.key] = authorizationHeader.value
} Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
.responseJSON { response in
debugPrint(response)
}
使用URLCredential认证
let user = "user"
let password = "password" let credential = URLCredential(user: user, password: password, persistence: .forSession) Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(usingCredential: credential)
.responseJSON { response in
debugPrint(response)
}

注意:使用URLCredential来做认证,如果服务器发出一个challenge,底层的URLSession实际上最终会发两次请求。第一次请求不会包含credential,并且可能会触发服务器发出一个challenge。这个challenge会被Alamofire接收,credential会被添加,然后URLSessin会重试请求。

将数据下载到文件

Alamofire可以把服务器的数据下载到内存(in-memory)或者硬盘(on-disk)中。所有Alamofire.requestAPI下载的数据都是存储在内存中。这比较适合小文件,更高效;但是不适合大文件,因为大文件会把内存耗尽。我们要使用Alamofire.downloadAPI把服务器的数据下载到硬盘中。

下面这个方法只适用于macOS。因为在其他平台不允许在应用沙盒外访问文件系统。下面会讲到如何在其他平台下载文件。

Alamofire.download("https://httpbin.org/image/png").responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
下载文件存储位置

我们可以提供一个DownloadFileDestination闭包把临时文件夹的文件移动到一个目标文件夹。在临时文件真正移动到destinationURL之前,闭包内部指定的DownloadOptions将会被执行。目前支持的DownloadOptions有下面两个:

  • .createIntermediateDirectories:如果指定了目标URL,将会创建中间目录。
  • .removePreviousFile:如果指定了目标URL,将会移除之前的文件
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
} Alamofire.download(urlString, to: destination).response { response in
print(response) if response.error == nil, let imagePath = response.destinationURL?.path {
let image = UIImage(contentsOfFile: imagePath)
}
}

也可以直接使用建议的下载目标API:

let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)
下载进度

所有的DownloadRequest都可以使用downloadProgressAPI来反馈下载进度。

Alamofire.download("https://httpbin.org/image/png")
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}

downloadProgressAPI还可以接受一个queue参数来指定下载进度闭包在哪个DispatchQueue中执行。

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
恢复下载

如果一个DownloadRequest被取消或中断,底层的URL会话会生成一个恢复数据。恢复数据可以被重新利用并在中断的位置继续下载。恢复数据可以通过下载响应访问,然后在重新开始请求的时候被利用。

重要:在iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1中,resumeData会被后台URL会话配置破坏。因为在resumeData的生成逻辑有一个底层的bug,不能恢复下载。具体情况可以到Stack Overflow看看。

class ImageRequestor {
private var resumeData: Data?
private var image: UIImage? func fetchImage(completion: (UIImage?) -> Void) {
guard image == nil else { completion(image) ; return } let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
} let request: DownloadRequest if let resumeData = resumeData {
request = Alamofire.download(resumingWith: resumeData)
} else {
request = Alamofire.download("https://httpbin.org/image/png")
} request.responseData { response in
switch response.result {
case .success(let data):
self.image = UIImage(data: data)
case .failure:
self.resumeData = response.resumeData
}
}
}
}

上传数据到服务器

使用JOSN或者URL编码参数上传一些小数据到服务器,使用Alamofire.request API就已经足够了。如果需要发送很大的数据,需要使用Alamofire.upload API。当我们需要在后台上传数据时,也可以使用Alamofire.upload

上传数据
let imageData = UIPNGRepresentation(image)!

Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上传文件
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上传多部分表单数据
Alamofire.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(unicornImageURL, withName: "unicorn")
multipartFormData.append(rainbowImageURL, withName: "rainbow")
},
to: "https://httpbin.org/post",
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
}
case .failure(let encodingError):
print(encodingError)
}
}
)
上传进度

所有的UploadRequest都可以使用uploadProgressdownloadProgress APIs来反馈上传和下载进度。

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post")
.uploadProgress { progress in // 默认在主线程中执行
print("Upload Progress: \(progress.fractionCompleted)")
}
.downloadProgress { progress in // 默认在主线程中执行
print("Download Progress: \(progress.fractionCompleted)")
}
.responseJSON { response in
debugPrint(response)
}

统计指标

时间表

Alamofire在一个请求周期内收集时间,并创建一个Tineline对象,它是响应类型的一个属性。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.timeline)
}

上面的Timeline信息包括:

  • Latency: 0.428 seconds (延迟)
  • Request Duration: 0.428 seconds (请求时间)
  • Serialization Duration: 0.001 seconds (序列化时间)
  • Total Duration: 0.429 seconds (总时间)
URL会话任务指标

在iOS和tvOS 10和macOS 10.12中,苹果发布了新的URLSessionTaskMetrics APIs。这个任务指标封装了关于请求和响应执行的神奇统计信息。这个API和Timeline非常相似,但是提供了很多Alamofire没有提供的统计信息。这些指标可以通过任何响应去访问。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.metrics)
}

注意:这些API只能在iOS和tvOS 10和macOS 10.12中使用。所以,根据部署目标,可能需要加入版本判断:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
if #available(iOS 10.0. *) {
print(response.metrics)
}
}

cURL命令输出

调试平台问题很让人厌烦。庆幸的是,Alamofire的Request对象遵循了CustomStringConvertibleCustomDebugStringConvertible协议来提供一些非常有用的调试工具。

CustomStringConvertible
let request = Alamofire.request("https://httpbin.org/ip")

print(request)
// GET https://httpbin.org/ip (200)
CustomDebugStringConvertible
let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
debugPrint(request)

输出:

$ curl -i \
-H "User-Agent: Alamofire/4.0.0" \
-H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
-H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \
"https://httpbin.org/get?foo=bar"

第一部分完。

作者:Lebron_James
链接:https://www.jianshu.com/p/f8c3adb056cf
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

【iOS开发】Alamofire框架的使用一基本用法的更多相关文章

  1. 李洪强iOS开发之 - enum与typedef enum的用法

    李洪强iOS开发之 - enum与typedef enum的用法 01 - 定义枚举类型 上面我们就在ViewController.h定义了一个枚举类型,枚举类型的值默认是连续的自然数,例如例子中的T ...

  2. iOS开发基础框架

    ---恢复内容开始--- //appdelegate ////  AppDelegate.m//  iOS开发架构////  Copyright © 2016年 Chason. All rights ...

  3. iOS开发多线程篇 08 —GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  4. 【iOS开发】Alamofire框架的使用二 高级用法

    Alamofire是在URLSession和URL加载系统的基础上写的.所以,为了更好地学习这个框架,建议先熟悉下列几个底层网络协议栈: URL Loading System Programming ...

  5. iOS开发-网络框架-b

    网络框架(以下称NJAFNetworking)是基于AFNetworking框架的简单封装,基本功能包括POST请求,GET请求,上传文件,下载文件,网络状态,缓存等. 为什么要使用NJAFNetwo ...

  6. iOS开发UIKit框架-可视化编程-XIB

    1. Interface Builder 可视化编程 1> 概述 GUI : 图形用户界面(Graphical User Interface, 简称GUI, 又称图形化界面) 是指采用图形方式显 ...

  7. 简单搭建iOS开发项目框架

    今天我们来谈谈如何搭建框架,框架需要做一些什么. 第一步:找到我们的目标我们的目标是让其他开发人员拿到手后即可写页面,不再需要考虑其他的问题. 第二步:我们需要做哪些东西各位跟着我一步一步来进行. 假 ...

  8. iOS开发-CoreMotion框架(加速计和陀螺仪)

    CoreMotion是一个专门处理Motion的框架,其中包含了两个部分加速度计和陀螺仪,在iOS4之前加速度计是由UIAccelerometer类来负责采集数据,现在一般都是用CoreMotion来 ...

  9. iOS开发-CoreMotion框架

    转自: CoreMotion是一个专门处理Motion的框架,其中包含了两个部分 加速度计和陀螺仪,在iOS4之前加速度计是由 UIAccelerometer 类 来负责采集数据,现在一般都是用Cor ...

随机推荐

  1. iOS学习——输入验证码界面封装

    在很多App中都有输入验证码的功能需求,最近项目需要也有这个功能.做完之后简单整理了一下,将实现的基本思路做下记录.实现后的效果大致如下图所示,当四位签到码全部输入时,提交按钮是可以提交的,否则提交按 ...

  2. Python内置函数(16)——dir

    英文文档: dir([object]) Without arguments, return the list of names in the current local scope. With an ...

  3. Netty(三) 什么是 TCP 拆、粘包?如何解决?

    前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...

  4. 你安装的是SUN/Oracle JDK还是OpenJDK?

    目录 1 如何查看你安装的JDK版本 1.1 要用到的命令行工具 1.2 查看JDK的版本 2 什么是 OpenJDK 2.1 OpenJDK 的来历 2.2 Oracle JDK的来历 3 Orac ...

  5. Android Native App自动化测试实战讲解(下)(基于python)

    6.Appuim自动化测试框架API讲解与案例实践(三) 如图1,可以在主函数里通过TestSuite来指定执行某一个测试用例: 6.1,scroll():如图2 从图3中可以看到当前页面的所有元素r ...

  6. 探讨.net Socket支持在线连接数量

    发现不少同学在用.NET做通讯的时候都关心一个问题,.NET能支持多少个在线连接.其实.net的通讯由winsocket所支持,既然由低层的winsocket所支持那.NET其端的接入连接数的受限完全 ...

  7. RabbitMQ消息队列(四)-服务详细配置与日常监控管理

    RabbitMQ服务管理 启动服务:rabbitmq-server -detached[ /usr/local/rabbitmq/sbin/rabbitmq-server -detached ] 查看 ...

  8. Linux下 Vim(Vi)编辑器的使用

    vi编辑器 vi是UNIX和类UNIX环境下的可用于创建文件的屏幕编辑器.vi有两种工作模式:命令模式和文本输入模式.启动vi需要输入vi,按[Spacebar]键并输入文件名后回车. 切换模式键 v ...

  9. mongodb副本集高可用架构

    一.简介 Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点. Mongodb Driver(客户端)的所有数据都写入Primary,Sec ...

  10. 在centos7中python3的安装注意

    ---恢复内容开始--- 云主机的python是2.7有些不方便,故要更换3,看到官网有3.6的包,就下载了. wget https://www.python.org/ftp/python/3.6.3 ...