Flutter 打包

app 图标替换

Android 图标更换

  • 替换图中路径的图片资源即可,
  • 注意图片名字需要是 ic_launcher
  • 每个尺寸对应不同的文件大小
  • 如果要修改启动图片名称需要先在主配置文件中修改

iOS 图标更换

修改app图标名称

  • 找到 Android 主配置文件
  • 修改 android:label=”app_flutter” 中名称

修改app 的名称

打包

生成key

Flutter中本地图片资源使用

新建图片文件目录

添加图片到项目

手动投入图片到新建的文件目录

声明图片资源

找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径

1
2
assets:
- lib/assets/ideaOne.png

使用图片资源

1
Image.asset('lib/assets/ideaOne.png')

Flutter页面导航

导航栏的使用

程序主入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyApp extends StatelessWidget {

// 声明参数
final List<String> items;

// 构造函数
MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法


// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter --',
// 基础组件学习
// home: MyLearningFlutterHome(),

home: FirstScreen(),
);
}
}

导航栏页面跳转参数传递

商品类

1
2
3
4
5
6
class Product {
final String title; // 定义商品标题
final String description;
// 定义构造方法
Product(this.title, this.description);
}

第一级页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: new Text('第一个页面'),
),

body: Center(
child: RaisedButton(
child: new Text('点击跳转详情页'),
onPressed: (){
Navigator.push(context, new MaterialPageRoute(
builder: (context) => (new SecondScreen())
// builder: (context){
// new SecondScreen();
// }
));
},
),
),
);
}
}

详情页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('详情页面')
),

body: Center(
child: RaisedButton(
child: Text('返回上一级页面'),

onPressed: () => (Navigator.pop(context)),

// onPressed: (){
// Navigator.pop(context);
// },
),
),
);
}
}

导航栏页面返回反向参数传递

使用异步请求和等待

商品列表页面

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
class ProductListView extends StatelessWidget {
// 定义变量,里面是一个对象
final List<Product> products;

// 使用构造方法 函数的方式接受参数
ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品列表'),
),

// 使用 ListView.builder 动态构建
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].title),
onLongPress: (){
print("长按事件响应");
},


// 点击时传入参数
onTap: (){
print('点击事件');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProducterDetailView(product:products[index])
)
);
},
);
},
),
);
}
}

商品详情页面点击按钮返回上一级页面,并带有参数

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
class ProducterDetailView extends StatelessWidget {
final Product product;

ProducterDetailView({Key key, @required this.product}):super(key:key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Product ${product.title} 详情页面')
),

body: Center(
// 有多控件
child: Column(
children: <Widget>[
Text('Product ${product.title} desc ${product.description}'),
RaisedButton(
child: Text('back Product List'),
color: Colors.blue,
onPressed: (){
Navigator.pop(context, 'selected Id = ${product.title}');
},
)
],
),
),
);
}
}

Flutter中布局

线性布局

主轴、副轴

  • main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴

  • cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴

Row 水平方向布局组件

  • 非自适应布局:根据元素本身大小来布局
  • 自适应布局:使用 Expanded 布局

非自适应布局

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
class MyRowNoChangeLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),

),

new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

),
new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

自适应布局

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
class MyRowAutoLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
)
],
),
);
}
}

非自动布局和自动布局混合使用

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
class MyRowLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

Column 垂直方向布局组件

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
class MyColunmLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.cyan,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
// 设置主轴的对齐方式
mainAxisAlignment: MainAxisAlignment.center,
// 设置横轴的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton (
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},
color: Colors.green,
child: new Text('按钮一绿色'),
),

new Text('文字内容',
textAlign: TextAlign.center,
),
new Text('测试文字',
textAlign: TextAlign.right,
)
],
),
);
}
}

层叠布局

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
46
47
48
49
50
51
52
// stack层叠布局

class MyStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 返回一个 Stack 布局
return new Stack(
// 设置位置
alignment: const FractionalOffset(0.5, .9),
// 圆角组件
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
// backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

// 容器组件
new Container(
// 设置容器掩饰
decoration: BoxDecoration(
color: Colors.blue,
// border
border: Border.all(
color: Colors.red,
width: 3,
),
),

padding: const EdgeInsets.all(10.0),

child: new Text('stack 布局',
textAlign: TextAlign.center,
maxLines: 1,
// 文字样式
style: TextStyle(
color: Colors.yellow,
textBaseline: TextBaseline.ideographic,
// 下划线
decoration: TextDecoration.underline,
// 下划线颜色
decorationColor: Colors.orange,
// 下划线样式
decorationStyle: TextDecorationStyle.double,
),
),
)
],

);
}
}

Positioned组件

  • bottom: 距离层叠组件下边的距离
  • left:距离层叠组件左边的距离
  • top:距离层叠组件上边的距离
  • right:距离层叠组件右边的距离
  • width: 层叠定位组件的宽度
  • height: 层叠定位组件的高度
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
class MyMoreStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

new Positioned(
top: 5,
left: 50,
// right: 5,
width: 100,
child: new RaisedButton(
onPressed: (){
print("多个组件层叠布局");
},

color: Colors.deepOrange,
child: Text('测试按钮'),
),
),

new Positioned(
bottom: 10,
right: 10,
left: 10,
child: new Text('flutter 多个试图 stack布局',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightGreen,
fontSize: 20,
fontStyle: FontStyle.italic
),
)
)
],
);
}
}

Card 布局

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class MyCardLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Card(
child: Column(
children: <Widget>[
new ListTile(
title: new Text('card 布局1'),
subtitle: new Text('子标题1',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_balance, color: Colors.purple)
),

new Divider(
color: Colors.cyan
),

new ListTile(
title: new Text('card 布局2'),
subtitle: new Text('子标题2',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_box, color: Colors.yellow)
),

new Divider(
color: Colors.red,
),

new ListTile(
title: new Text('card 布局3'),
subtitle: new Text('子标题3',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_circle, color: Colors.red)
),

new Divider(
color: Colors.orangeAccent,
),

new ListTile(
title: new Text('card 布局4'),
subtitle: new Text('子标题4',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.camera_enhance, color: Colors.brown)
)
],
),
);
}
}

GridView组件使用.md

GridView 组件使用

  • padding: const EdgeInsets.all(10.0) 设置边距为10
  • crossAxisCount:横轴子元素的数量crossAxisCount。
  • mainAxisSpacing:主轴方向的间距。
  • crossAxisSpacing:横轴元素的间距。
  • childAspectRatio:元素的宽高比

简单例子代码

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
class MyGridView2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: new GridView(
// 设置边距
padding: const EdgeInsets.all(10.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

// 横轴 数量
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10.0,
// 宽高比:默认是1.0
childAspectRatio: 1.5,
),

children: <Widget>[
new Image.network('https://github.com/Jinxiansen/SwiftUI/raw/master/images/example/Text.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/d6fdc891-eec3-47ce-b837-df726c73a9b8.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/0cccf224-57d1-4a3e-bf06-9df5fed0f4e0.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/2bf5c0d7-dc3a-45d2-ab1b-40fe737aa79a.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/a20bb53c-b710-4b39-9607-91bef06ee54e.png', fit: BoxFit.cover),
],
)
);
}
}

以前的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyGridView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: new GridView.count(
padding: const EdgeInsets.all(15.0),
// 横轴 item 数量
crossAxisCount: 3,
// 横轴 间距
crossAxisSpacing: 10.0,
children: <Widget>[
const Text('我是第一个'),
const Text('我是第二个'),
const Text('我是第三个'),
const Text('我是第四个'),
const Text('我是第五个'),
const Text('我是第六个'),
const Text('我是第七个'),
],
),
);
}
}

第十四章 第三方库

AFNetworking

框架图

会话NSURSession

主要类关系图

AFURLSessionManager

  • 创建和管理 NSURLSession, NSURLSessionTask
  • 实现NSURLSessionDelegate 等协议的代理方法
  • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
  • 引入AFNetworkReachabilityManager 监控网络状态

SDWebImage

架构图

加载图片流程

Reactive Cocoa

RACStream

AsyncDisplayKit

主要解决的问题

基本原理

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

使用场景

  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • 对类中的代码进行抽取分解
  • 把Framework的私有方法公开化

特点

  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • 可以为系统类添加方法
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • 名字相同的分类编译会报错

分类中可以添加的内容

  • 实例方法
  • 类方法
  • 协议
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

  • 给分类添加成员变量,通过关联对象的方式

扩展

使用场景

  • 声明私有属性
  • 声明私有方法
  • 声明私有成员变量

特点

  • 编译时决议
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • 不能为系统添加扩展

代理

  • 代理设计模式,传递方式是一对一
  • 使用weak 避免循环引用

通知

使用观察者模式来实现的用于跨层传递消息的机制

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

  • KVO 是OC对观察者模式的又一实现
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
  • 使用 setter 方法设置值KVO才能生效
  • 使用setValue:forKey: 改变值KVO才能生效
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

KVC

apple提供的键值编码技术

  • (nullable id)valueForKey:(NSString *)key;
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

属性关键字

读写权限

  • readonly

  • readwrite 系统默认

原子性

  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • nonatomic

引用技术

  • assign
    • 修饰基本数据类型,
    • 修饰对象时不改变其引用计数,
    • 会产生悬垂指针
  • weak
    • 不改变被修饰对象的引用计数,
    • 所指对象被释放之后指针自动置为nil

两者区别:

  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

问题1

weak 修饰的对象为什么释放后会自动置为nil

问题2

@property (copy) NSMutableArray *array; 有什么问题

  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

copy 关键词

  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
    • 会增加对象的引用计数
    • 并没有一个新的内存分配
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
    • 不会增加源对象的引用计数
    • 有新对象的内存分配

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

第十章 网络请求相关

HTTP协议

超文本传输协议

请求/响应报文

请求报文:

  • 请求行
    方法 URL 协议版本(1.1版本) CRLF(回车)

  • 首部字段区域
    首部字段名(key) :值(value )CRLF
    首部字段名(key) :值(value )CRLF

  • 实体主体
    get 请求没有
    post 有实体主体

响应报文:

  • 响应行
    版本 状态码 状态码描述(短语) CRLF

  • 首部字段区

  • 响应实体主体

连接建立流程

三次握手和四次挥手

三次握手

  • 客户端 发送 syn 同步报文到Server,
  • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
  • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

数据请求

  • 接下来在已经建立的tcp 通道上进行http 的请求报文
  • server 端回复给客户端一个http响应报文

四次挥手

  • 客户端发送 fin 终止报文到server 端
  • server 端收到报文后会回回复一个ack 报文到客户端
  • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
  • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

HTTP 的特点

  • 无连接
    非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

    HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

    头部字段:

    coonection : keep-alive 客户端是否采用持久链接
    time :20 多长时间有效
    max:10 最多发生多少个http请求和响应对
    
  • 无状态
    同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

    • cookie
      cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

      保证cookie 的安全

      • cookie 加密处理
      • 只在https 上携带Cookie
      • 设置Cookie为 httpOnly,防止跨站脚本攻击
    • session 用来记录用户的状态,区分用户,状态存放在服务端

HTTPS 与网络安全

HTTPS 都使用了那些加密手段,为什么?

  • 建立链接过程使用非对称加密,因为耗时
  • 后续通信数据传输过程中使用对称性加密

对称性加密

TCP: 传输控制协议

  • 面向链接
    TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
  • 可靠传输

    • 无差错
    • 按序到达
  • 面向字节流

    发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

  • 流量控制

    滑动窗口协议

UDP : 用户数据协议

  • 无连接
    发送数据时不需要建立链接,传输完毕也不需要释放链接

  • 尽最大努力交付
    是不保证可靠传输的,

  • 面向报文
    既不合并,也不拆分

问题

GET 和post 请求的区别

  • GET 获取资源的

    • 安全的: 不因该引起Server 端的任何状态变化的
    • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
    • 可缓存的:请求是可以缓存的,代理服务器可以缓存
    • 请求参数以? 分割拼接到URL 后面
  • post 请求处理资源

    • 不安全的:
    • 非幂等的
    • 不可缓存的

GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

HTTPS 链接建立流程

客户端会发送服务端一个支持的加密算法列表
包括 TLS 的版本号 + 随机数C,
服务端再回给客户端一个证书
包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

TCP 和 UDP的区别

tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
UDP 提供了简单的复用/分用及差错检测的传输层的功能,

状态吗有哪些

  • 1XX
  • 2XX:成功
  • 3XX:重定向
  • 4XX:客户端请求错误
  • 5XX: 服务器端请求错误

为什么需要三次握手,两次行不行?

解决同步请求链接建立时超时的情况,如果只有两次握手,

持久链接怎么判断一个请求是否结束

  • 请求/响应报文的头部字段

响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

  • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

Charels 抓包原理是什么?

中间人攻击

正常步骤是 客户端 —- server

抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

客户端 —中间人—- server

DNS 解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

NDS 解析存在的常见问题

  • DNS 劫持问题
    钓鱼网站
  • DNS 解析转发问题

DNS 劫持和HTTP 的关系时怎么样的

没有关系,

  • DNS 解析发在HTTP 建立连接之前
  • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

解决DNS 劫持问题

  • httpDNS
  • 长连接

第十一章 设计模式

六大设计原则

单一职责原则:一个类只负责一件事

开闭原则: 对修改关闭,对扩展开放

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

常用设计模式

责任链

有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

桥接模式

业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

类构成

抽象类A ——> class B

适配器模式

一个现有类需要适应变化的问题, 年代比较久远的类或者对象

类构成

  • 对象适配
  • 类适配

单例模式

命令模式

行为参数化

降低代码重合度

第十二章 架构和框架

目的

  • 实现模块化
  • 分层
  • 解耦

图片缓存

图片缓存框架设计方案?

图片缓存时内存设计上需要考虑的问题

  • 内存存储的size

    • 10kb的图片开辟的内存空间 50 张
    • 100kb图片的 20 张
    • 100kb 以上的 10 张
      通过队列的方式存储,先进先出的方式淘汰
  • 淘汰策略

    • 以队列先进先出的方式淘汰
    • 已LRU 算法 (如 30 min 之内是否使用过)

磁盘设计考虑的问题

  • 存储方式
  • 大小限制
  • 淘汰策略(超过 7 天)

网络部分的设计需要考虑的问题

  • 图片请求最大并发量
  • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
  • 请求优先级,下载或者缓存的图片是否紧急需要

图片通过什么方式进行读写,过程是怎样的?

  • 以图片的url 的单向哈希值作为key 存储
  • 读取过程

图片解码

  • 对于不同格式的图片,解码采用什么方式来做?

    • 应用策略模式对于不同图片格式进行解码
  • 在那个阶段做图片解码处理?

    • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

线程处理

阅读时长统计

怎样设计一个时长统计框架?

为何要有不同类型的记录器,处于什么考虑?

基于不同分类场景提供的关于记录的封装、适配

记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

  • 定时写磁盘
  • 限定内存缓存条数,超过该条数就写如磁盘

记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

上传时机

  • 立刻上传
  • 延时上传
  • 定时上传

延时上传场景

统计结果立即上传会重复调用接口导致资源浪费

  • 前后台切换
  • 从无网到有网的变化时
  • 通过其他的接口捎带着上传

复杂页面的架构

MVVM 框架思想

RN 的数据流思想

任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

系统UIView 更新机制的思想

AsyncDisplayKit 关于预排版的设计思想

客户端整体架构

需要独立于APP 的通用层
通用的业务层
中间层
业务层

业务之间的解耦通信方式

  • OpenURL
  • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信