PEI丶BLOG

Happy coding!


  • 首页

  • 标签

  • 分类

  • 归档

Swift关键字

发表于 2019-08-23 | 分类于 iOS , Life , Job

Swift 关键字

inout

将值类型的对象按照应用类型传递,以按址传递的方式进行操作。

1
2
3
4
5
6
var x = 10
func test(a: inout Int) {
a += 1
print(a)
}
test(a: &x) //注意,取地址。 输出 11

除了 Int类型,诸如:CGFloat,Bool,Character,Array,struct等,这些值类型的对象都可以使用inout修饰,达到使用引用的方式传递的目的。

defer

修饰一段函数内任一段代码,使其必须在函数中的其余代码都执行完毕,函数即将结束前调用。也可以理解成将延迟执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func testDefer() {
print("start")
defer{
print("执行defer1")
}
print("函数结束")
defer{
print("执行defer2")
}
print("end")
}
testDefer()
//start
//函数结束
//end
//执行defer2
//执行defer1

throws
Swift中提供了Error协议,我们在开发中,如果要自定义自己的错误类型,一般会使用一个Enum来继承Error协议,目的是享用Error已经包含的一些特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
enum MyError: Error {
case error1
case error2
case error3
}
//throws的使用很简单,只需要在可能出现异常的函数或者方法后面添加throws。
经过这个关键字修饰的函数,在调用的时候,需要程序员加上do-catch来调用。
对于错误类型开发者来说,只需要使用Throws进行修饰,就保证了以后的调用者
必然需要对相应的错误进行处理(当然也可以不处理,但无论如何,错误被throw
携带出来了,以后的维护和优化不需要重新做错误处理的设计,直接加上错误处理的逻辑即可)。

func throwError(type: MyError)throws -> String{
switch type {
case .error1:
throw MyError.error1
case .error2:
throw MyError.error2
case .error3:
throw MyError.error3
}
}

do {
try throwError(type: .error1)
} catch let err as MyError {
print(err)
}

一般在请求网络时,会自定义错误类型,返回错误原因。

Swift中的值类型和引用类型

发表于 2019-08-13 | 分类于 iOS

值类型和引用类型

首先,需要了解Swift中的值类型和引用类型。

在Swift中
值类型: 基本数据类型(Int,Double,String,Array,Dictionary)
引用类型: class, block, function

  • 值类型(Value Types):每份实例都保留了一份独有的数据拷贝,一般以结构体(struct)、枚举(enum)或者元组(tuple)的形事出现。
  • 引用类型(Referance Types):每个实例共享同一份数据来源,一般以类(class)的形式出现。

Copy on Write(写时复制)

  • Copy-on-Write是一种用来优化占用内存大的值类型的拷贝操作的机制。
  • 对于Int、String等基本类型的值类型,它们在赋值的时候就会发生拷贝,它们没有Copy-on-Write这一特性(因为Copy-on-Write带来的开销往往比直接复制的开销要大)。
  • 对于Array、Dictionary、Set类型,当它们赋值的时候不会发生拷贝,只有在修改的之后才会发生拷贝,即Copy-on-Write。
  • 在swift中,当你有一个占用内存很大的一个值类型,并且不得不将它赋值给另一个变量或者当做函数的参数传递的时候,拷贝它的值是一个非常消耗内存的操作,因为你不得不拷贝它所有的东西放置在另一块内存中。
  • 为了优化这个问题,Swift对于一些特定的值类型(集合类型:Array、Dictionary、Set)做了一些优化,在对于Array进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int等值类型,在赋值的时候就会发生拷贝。

存储方式

  1. 值类型和引用类型在内存上存储的地方不一样。值类型的值是存储在内存的栈当中。引用类型的值是存储在内存的堆中。
  2. 在传递值类型和传递引用类型的时候,传递方式不一样。值类型我们称之为值传递,引用类型我们称之为引用传递。
  3. 引用类型的对象存储在堆中,并且会分配一个内存地址。该内存地址会存储到栈空间,栈空间名为变量名。
    即读取引用类型对象的顺序:变量—>内存地址—>实例对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//打印内存地址
func address(of object: UnsafeRawPointer) -> String {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}

var number = 10
var number2 = number
print(address(of: &number), address(of: &number2))
//打印内存地址为 number=0x1199d2030; number2=0x1199d2038
栈
------------
|number(oxa) | ----|
|------------| |
|number2(oxa)| ----|
|------------| |
| | /
|------------| /
| oxa(10) |<--
------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let p = Person(name: "11")
let p1 = p
p1.name = "22"
print(p.name, p1.name)// 22 22

栈 堆
---------- ----------
| p |---->| Person |
|----------| | |----------|
| p1 |__| | name = 11|
|----------| | |
| | | |
---------- ----------

其中,如果引用类型里面包含值类型,值类型会存在引用类型的存储区中。

回校毕业啦

发表于 2019-06-05

今天刚完成设计答辩,今天刚得知消息:17号总答辩,26号毕业典礼。
怎么说呢,接下来的23天,或许就是我大学生涯的最后时期了。曾经憧憬过自己的大学会怎样怎样度过。现在想想,嗯,在学业上,表现的还可以,不能说满意,至少没有荒废。在实验室的日子,逼着自己学习,那段时间很迷惘,觉得学完iOS就可以找到一个不错的工作,其实,社会一直在变,IT行业更是如此,新技术不断更新,作为一名准程序员,必须时刻保证自己的掌握的技术要与时俱进。在北京的面试中,就可以感受到,对实习生的要求也在慢慢提高,只有在每一次的面试中,总结自己在哪些方面有些欠缺,及时去补充知识。
18年7月16号,在北京的第一天,也是我的幸运日,现在的公司要我了,也很感激老大,给了我第一份实习工作。从最开始的swift小白,到现在能够开发,也挺喜欢Swift这门语言,使用起来很简洁,快速。第一次接触公司的项目,发现和自己的知识面还是不够,只能勉强参与开发,必须抓紧熟悉公司的项目,看着一大堆文件,无从下手,只好自己一个一个看,熟悉功能的实现。开始很小心翼翼,融入一个新的环境,不过很开心,每个同事都很好,像导师,指导我,挑出我的问题,很感谢他们。在生活方面,虽说住得远了点,但周末还可以自己学的做饭。我记得小时候,总听到家人说,你看,那个那个小孩都会自己做饭了,你怎么不学一下,我总是不屑一顾,我觉得做饭这个东西,我现在不需要,可以不去接触它,现在证明,当我需要的时候,我也可以学会做饭。不过一个人的时候,真的很无聊,看着来北京的同学一个个都走了,哎,也不知道怎么说,生活不易!在工作方面,我容易满足,大厂的诱惑力很大,我却没有为之付出,觉得只要做好现在的工作就好了,可能心中也没有特别想去吧,说到底,还是自己太懒了,不想刷题,不想去专研,所以才不够。在爱情上,能遇到孙皮皮,我很幸运,在她身上,有很多优点,正是我所欠缺的,我们可以相辅相成,为了我们的美好生活去奋斗。我一直希望结束异地恋,觉得两个人在一起,会更加幸福,但是目前,压力还是很大的,感觉只有找出一个我们两个人都能赚钱的方法,现实就是这样,不过我们能够打破这个现实的屏障,爱情事业双丰收!
北京这一年,辛苦也幸福,总之,只要你努力点,生活会给你开一扇窗,加油吧,北京可以包容每一个努力的人!

Alamofire 结构解读

发表于 2019-04-26 | 分类于 源码解读

源码阅读,我觉得首先了解 它的框架,结构,或许就是架构吧。

  • request,download,upload,
    iOS进行网络请求:URLSession,创建session,sessionconfig,request(请求体,url,方法等)。
    查看请求的数据data,有两种方式,一种是代理,另一种是block(您的URLSession对象不需要委托。如果没有分配委托,则使用系统提供的委托,并且必须提供一个completion回调函数来获取数据)。
    1
    2
    初始化方法:
    URLSession(configuration: URLSessionConfiguration, delegate: URLSessionDelegate?, delegateQueue: OperationQueue?)

URLSession:支持data file ftp http 和 https的url,支持代理服务和SOCKS通道。并被配置到用户的系统中;支持HTTP/1.1,SPDY,HTTP/2协议。HTTP/2的支持被描述在RFC 7540,并且需要一个服务支持,无论是ALPN还是NPN。
首先,要建立HTTP连接,需要用到URLSession;每一个被指定了URL的session的任务,需要共享一个URLSessionConfiguration,这个object定义了网络链接的行为,例如,对于同一个host最大并发连接数量等;


URLSessionTask是一个表示任务对象的抽象类,一个会话创建一个任务,这里任务是指获取数据、下载或上传文件。
有三种类型的会话
URLSessionDataTask: 处理从HTTP get请求中从服务器获取数据到内存中。
URLSessionUploadTask:上传硬盘中的文件到服务器,一般是HTTP POST 或 PUT方式
URLSessionDownloadTask: 从远程服务器下载文件到临时文件位置。


AFError.swift
里面定义了很多Alamofire请求返回的错误类型。
包括:判断url是否可用,response序列化,response失败原因,参数编码失败等,
描述的localizedDescription,会返回错误原因。


目录结构
Alamofire目录.png

备注
AFError

Alamofire


Alamofire.swift
进行网络请求的任务,所有的方法都封装在这。
内部也是通过SessionManager来处理请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public protocol URLConvertible {
/// Returns a URL that conforms to RFC 2396 or throws an `Error`.
///
/// - throws: An `Error` if the type cannot be converted to a `URL`.
///
/// - returns: A URL or throws an `Error`.
func asURL() throws -> URL
}
request,download,upload,
SessionManager:负责创建和管理“Request”对象,以及他们底层“NSURLSession”
open static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
defaultHTTPHeaders:
return [
"Accept-Encoding": acceptEncoding,
"Accept-Language": acceptLanguage,
"User-Agent": userAgent
]
///request请求
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}

Request:负责发送和接收从服务器返回的响应和相关数据,以及管理他的底层“URLSessionTask”,还有分类DataRequest,负责请求回来的数据

关于生活的感悟--五月篇

发表于 2019-04-26 | 分类于 生活

目前,需要在公司完成毕业设计,还想要去毕业旅行。
暂定广州、香港等

iOS成长之路(一)

发表于 2019-04-26 | 分类于 iOS

一、基础知识篇

  • iOS基本数据结构
    (1)swift中,字典的定义:

    1
    struct Dictionary<Key, Value> where Key : Hashable(Key要遵守Hashable协议)
  • 内存管理
    @autoreleasepool{}

    1
    2
    3
    4
    5
    struct __AtAutoreleasePool {
    __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
    ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
    void * atautoreleasepoolobj;
    };

自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的
在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中
调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息

  • @synthesize和@dynamic
    @property定义属性后,会自动合成setter和getter方法,和带下划线的实例变量。
    @synthesize的作用:
    (1)让编译器自动生成setter和getter方法
    (2)指定与属性对应的实例变量
    @dynamic的作用:
    (1)告诉编译器,属性的setter和getter方法自己实现
    (2)如果没有提供对应的setter和getter方法,编译不会错,运行时才会报错

  • 属性关键字
    weak,strong,copy,assign

  • runtime
    SEL: 是方法的表示形式
    IMP: 是一个函数指针,指向方法的实现
    Cache: 一个存储Method的链表,主要是为了优化方法调用性能

  • OC消息传递
    首先,需要知道OC的方法调用,通常是这种形式;
    Son *son = [[Son alloc] init] son->isa == Son objc_msgSend(id self, SEL cmd, ...) id returnValue = [someObject messageName:parameter];
    实例对象调用一个方法,会在父类对象的方法列表里面找。一直到顶端的父类。如果没有找到会抛出“unrecognized selector sent to XXX”异常!!!
    不过在此,提供三次拯救机会:

    (1)Method resolution:
    · 你可以通过实现 +resolveInstanceMethod: 以及 +resolveClassMethod: 方法以动态的实现一个指定实例的实例方法或者类方法。
    · 一般说来,消息转发机制 和 动态加载机制 是正交的。在转发机制介入之前,一个类有机会动态的加载一个方法。
    (2)Fast Forwarding
    如果对象实现 -forwardingTargetForSelector:,runtime就会调用这个方法,给把这个消息转发给其他对象的机会。
    (3)Normal forwarding

  • swift消息派发
  • KVC和KVO
    KVC:键值编码(NSKeyValueCoding),通过key值,来获取对象的属性进行操作,而不是通过我们明确的存取方式来获取,是一个非正式的Protocol。KVO就是基于KVC来实现的。

  • KVC使用原则:在调用 setValue: forKey: 的时,程序优先调用 setName: 方法,如果没有找到 setName: 方法 KVC会检查这个类的 + (BOOL)accessInstanceVariablesDirectly 类方法看是否返回YES(默认YES),返回YES则会继续查找该类有没有名为_name的成员变量,如果还是没有找到则会继续查找_isName成员变量,还是没有则依次查找name,isName。上述的成员变量都没找到则执行setValue:forUndefinedKey: 抛出异常,如果不想程序崩溃应该重写该方法。假如这个类重写了+ (BOOL)accessInstanceVariablesDirectly 返回的是NO,则程序没有找到setName:方法之后,会直接执行setValue:forUndefinedKey: 抛出异常。
    在调用valueForKey:的时,会依次按照getName,name,isName的顺序进行调用。如果这3个方法没有找到,那么KVC会按照countOfName,objectInNameAtIndex来查找。如果查找到这两个方法就会返回一个数组。如果还没找到则调用+ (BOOL)accessInstanceVariablesDirectly 看是否返回YES,返回YES则依次按照_name,_isName,name,isName顺序查找成员变量名,还是没找到就调用valueForUndefinedKey:;返回NO直接调用valueForUndefinedKey:

  • KVO原理(Key-Value Observing)
    KVO的底层是通过isa-swizzling实现的。替换类的isa指针指向NSKVONotifying_ClaaName,是他的子类。然后将被观察的类对象的isa指针指向这个子类。再重写了setter方法。并在当中添加了willChangeValueForKey:和didChangeValueForKey:。
    移除观察就是将isa指针指向原来的类对象中。

  • Notifaction原理
  • RunLoop

    A RunLoop object processes input for sources such as mouse and keyboard events from the window system, Port objects, and NSConnection objects. A RunLoop object also processes Timer events.

NSRunLoop就是一个对象,不是线程安全的。
是什么:一个Runloop就是一个事件处理的循环,用来不停的调度工作及处理输入事件。
Mode:一个Runloop有几个Mode,但同一时刻只能跑在一个mode下,处理Source,Timer和Observe。

Runloop5个mode 备注
NSDefaultRunLoopMode 默认的mode,正常情况下都是在这个mode
NSConnectionReplyMode
NSModalPanelRunLoopMode
NSEventTrackingRunLoopMode 使用这个Mode去跟踪来自用户交互的事件(比如UITableView上下滑动)
NSRunLoopCommonModes 所有mode的集合

iOS 中公开暴露出来的只有 NSDefaultRunLoopMode 和 NSRunLoopCommonModes 。 NSRunLoopCommonModes 实际上是一个 Mode 的集合,默认包括 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode

  • iOS响应链和事件传递
    首先我们需要知道UIResponder对象。只有继承UIResponder,才能处理事件。UIApplication,UIView,UIViewController。CALayer不是UIResponder的子类,无法处理事件。
    事件传递:1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
    2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
    3.UIWindow将事件向下分发,即UIView。
    4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
    5.遍历子控件,重复以上两步。
    6.如果没有找到,那么自己就是事件处理者。如果
    7.如果自己不能处理,那么不做任何处理。
    其中 UIView不接受事件处理的情况主要有以下三种
    1)alpha <0.01
    2)userInteractionEnabled = NO
    3.hidden = YES.

    响应者链:从最合适的View开始传递给下一任响应者,如果所有的响应者都不处理事件,则事件将会被丢弃。UIResponder的nextResponder方法。

  • block捕获变量
    ARC下, block种类

    _NSConcreteStackBlock 只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
    StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。
    _NSConcreteMallocBlock: 有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制 。
    _NSConcreteGlobalBlock: 没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。
    没有用到外部变量肯定是_NSConcreteGlobalBlock,这点很好理解。不过只用到全局变量、静态变量的block也是_NSConcreteGlobalBlock

block捕获变量的方式有两种:1.传递内存地址指针到Block中;2.改变存储区方式(__block).

  • 多线程、死锁
    GCD:同步、异步、串行、并发
    NS
  • realm数据库
  • fastlane 自动化打包

输入URL后,接下来会发生什么--经典前端面试题

发表于 2019-04-25 | 分类于 网络请求

问题:在浏览器输入一个网址,接下来会做什么?

Step1: DNS(Domain Name System)解析

1
2
DNS(Domain Name System)服务是和HTTP协议一样位于应用层的协议。它提供域名到IP地址之间的解析。
用户通常使用主机名或域名来访问对方的计算机,而不是通过IP地址访问。

DNS作用.png

1
2
首先,找缓存。浏览器缓存,系统缓存,路由器缓存,ISP DNS 缓存, 递归搜索(你的ISP(Internet Service Provider)的DNS从根域名服务器开始进行递归搜索,从.com顶级域名服务器到对应网址的域名服务器)
DNS解析是一个递归查询的过程

完整请求过程

完整的网络请求.png

Step2-TCP连接

1
2
3
HTTP协议作为TCP作为传输层协议。HTTP报文是包裹在TCP报文中发送;
服务器在收到TCP报文时提取出HTTP报文;
HTTPS则在进入TCP报文之前对HTTP做一次加密,使用SSL或TLS对HTTP报文加密。

HTTP请求报文: 请求行,请求报文和请求正文

  • 请求行:GET index.html HTTP/1.1
  • 请求报文:Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等;
  • 请求正文:一般客户端想服务器传递数据,这些数据就存储在请求正文中。
    image.png

HTTP响应报文: 状态码,响应报头报文和响应正文

  • 状态码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    1xx:指示信息–表示请求已接收,继续处理。
    2xx:成功–表示请求已被成功接收、理解、接受。
    200:请求成功
    204:无内容。
    3xx:重定向–要完成请求必须进行更进一步的操作。
    301:永久性转移
    302:暂时性转移
    304:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
    4xx:客户端错误–请求有语法错误或请求无法实现。
    400:客户端请求的语法错误,服务器无法理解
    401:请求要求用户的身份认证
    403:服务器理解请求客户端的请求,但是拒绝执行此请求
    404:请求资源不存在
    422:参数错误,同400
    5xx:服务器端错误–服务器未能实现合法的请求。
    500:内部服务器错误
    501:服务器不支持请求的功能,无法完成请求
    503:服务器停机维护
  • 响应报头
    Server,Content-Type等

  • 响应报文
    服务器返回给浏览器的文本信息,通常HTML,CSS,图片等
    响应头.png

Step3-浏览器渲染

显示过程

iOS URL Loading System

发表于 2019-04-25 | 分类于 源码解读

###目的: 了解iOS如何进行网络请求

1
首先,你得知道HTTP之前的通信过程是怎么样的,具体参考我之前写过的文章。

URL Loading System

Interact with URLs and communicate with servers using standard Internet protocols.
URLSession
使用标准Internet协议与url交互并与服务器通信。

简述:
URL加载系统使用标准协议(如https或您创建的自定义协议)提供对URL标识的资源的访问。加载是异步执行的,因此您的应用程序可以保持响应性,并在传入数据或错误到达时处理它们。
您可以使用一个URLSession实例来创建一个或多个URLSessionTask实例,该实例可以将数据获取并返回到您的应用程序、下载文件或将数据和文件上载到远程位置。要配置会话,可以使用URLSessionConfiguration对象,该对象控制诸如如何使用缓存和cookie或是否允许在蜂窝网络上连接等行为。
您可以重复使用一个会话来创建任务。例如,web浏览器可能有用于常规浏览和私有浏览的单独会话,其中私有会话不缓存其数据。图1显示了使用这些配置的两个会话如何创建多个任务。
URLSession 创建任务.png
每个会话都与一个委托相关联,以接收定期更新(或错误)。默认委托调用您提供的完成处理程序块;如果您选择提供自己的自定义委托,则不会调用此块。

1
这里就说到,如果你使用代理来接受数据,那么block就不会执行。二者只能选择其中一个(URLSession执行task时,关于数据传输的两只方式)。

You can configure a session to run in the background, so that while the app is suspended, the system can download data on its behalf and wake up the app to deliver the results.

Fetching Website Data into Memory

通过从URL会话创建数据任务,直接将数据接收到内存中。

Receive Results with a Completion Handler

CompletionHandler.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func startLoad() {
let url = URL(string: "https://www.example.com/")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
self.handleClientError(error)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
self.handleServerError(response)
return
}
if let mimeType = httpResponse.mimeType, mimeType == "text/html",
let data = data,
let string = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
self.webView.loadHTMLString(string, baseURL: url)
}
}
}
task.resume()
}

Receive Transfer Details and Results with a Delegate

在创建数据任务时,您可以在会话上设置委托,而不是提供完成处理程序,以便在任务进行时对任务的活动进行更大程度的访问。

通过代理接收传输详细信息和结果.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.default
configuration.waitsForConnectivity = true
return URLSession(configuration: configuration,
delegate: self, delegateQueue: nil)
}()
var receivedData: Data?

func startLoad() {
loadButton.isEnabled = false
let url = URL(string: "https://www.example.com/")!
receivedData = Data()
let task = session.dataTask(with: url)
task.resume()
}

// delegate methods
///验证响应是否具有成功的HTTP状态代码,MIME类型是否为text/html或text/plain。如果两者都不是,任务就被取消;否则,它被允许继续。
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode),
let mimeType = response.mimeType,
mimeType == "text/html" else {
completionHandler(.cancel)
return
}
completionHandler(.allow)
}
///获取任务接收到的每个数据实例,并将其附加到名为receivedData的缓冲区中。
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.receivedData?.append(data)
}
///首先查看是否发生了传输级错误。如果没有错误,它将尝试将receivedData缓冲区转换为字符串并将其设置为webView的内容。
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
DispatchQueue.main.async {
self.loadButton.isEnabled = true
if let error = error {
handleClientError(error)
} else if let receivedData = self.receivedData,
let string = String(data: receivedData, encoding: .utf8) {
self.webView.loadHTMLString(string, baseURL: task.currentRequest?.url)
}
}
}
LPZ

LPZ

Record work, life and love!

8 日志
6 分类
9 标签
GitHub Weibo
Links
  • Acan’s blog
  • OneV's Den
© 2019 LPZ
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4