IRIS's BLOG

王圆圆的每日报告


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

计算机基础 - 0 操作系统

发表于 2019-03-03 | 分类于 实习 | | 阅读次数:
字数统计: 4k | 阅读时长 ≈ 13

题目合集

进程管理

一、进程和线程的区别

  • 按照操作系统的概念:
    • 进程为一个独立执行单元(是系统进行资源调度和分配的基本单位),在PC或者移动端是程序和应用的执行。
    • 线程为CPU调度的最小单元,是有限资源。一个进程可以包含多个线程。
  • 异:
    • 组成:唯一标示为进程控制块PCB + 资源、线程控制块TCB(栈)
    • 资源:进程间的资源是分隔独立的,内存映射表不同,占用物理内存地址是分隔的;线程的资源是共享所在进程的。
    • 切换:线程切换了TCB(栈结构),进程还包括切换资源,即切换内存映射表
  • 同:
    • 运行状态:就绪、运行、阻塞。运行时受阻如需要资源之后会变为阻塞,获取资源后变为就绪,再次运行。
  • iOS中的进程和线程
    • 进程:一个iOS程序运行后,该运行的程序就是进程。默认会开启1条线程,称为“主线程”
    • 线程:从用途上来说,线程分为主线程和子线程,主线程的作用是「显示刷新页面;处理UI事件」,而子线程的作用则是「执行耗时任务,比如网络请求、I/O 操作等」。如果在主线程中执行耗时操作那么就会导致程序无法及时地响应。因此耗时操作必须放在子线程中执行。
    • 多线程的实现:pthread,NSthread,GCD,NSOperation。

二、进程通信

(一)广义的进程间通信

本地进程间通信的方式有很多,可以总结为下面四类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

(二)狭义的进程间通信

1、共享存储器系统

A、共享数据结构:信息交换的格式、类型一定;进程通信由程序员完成,效率低;

B、共享存储区:进程可随时向系统申请一块存储区,并指定该分区的关键字,用于进程通信。

2、消息传递系统

A、直接通信方式 Send(Receiver,message);Receive(Sender,message);

B、间接通信方式:1、信箱的创建、撤消; 2、消息的发送和接受;Send(mailbox,message);

3、管道通信

管道:用于连接一个读进程和一个写进程,以实现它们之间通信的共享文件,又称为Pipe文件。

功能:Command1进程以字符流的形式向管道发送大量的数据,Command2进程则从管道接收数据。两进程实现单向、同步、互斥运行。单向:Command1只能发送;同步:管道满时,Command1等待;互斥:同一时刻,只能有一个进程对管道操作;

4、消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5、套接字(socket):套接口也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同的主机间的进程通信。通过”套接字”向网络发出请求或者应答网络请求。

三、死锁

在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。

✨产生条件:互斥、占有等待、非剥夺和循环等待。

  • 互斥条件 :一个资源一次只能被一个进程使用
  • 请求保持条件 :一个进程因请求资源而阻塞时,对已经获得资源保持不放
  • 不可抢占条件 :进程已获得的资源在未使用完之前不能强行剥夺
  • 循环等待条件 :若干进程之间形成一种头尾相接的循环等待资源的关系

✨死锁处理:

破坏产生死锁的4个必要条件中的一个或者多个

  • 预防死锁:预先静态分配法(破坏不可剥夺条件)、资源有序分配法(将资源分类按顺序排列,保证不形成环路)。
  • 避免死锁:银行家算法(对每个资源请求进行检测,确保安全。需要很大的系统开销)。
  • 检测死锁:资源分配图简化,非孤立点的进程处于死锁状态。
  • 解除死锁:资源剥夺法、撤销进程法。

四、CPU调度算法 (在就绪序列中怎么挑选进程让CPU执行)

先来先服务调度算法FCFS:按作业或者进程到达的先后顺序依次调度;(平均周转时间可能会很长 )

短作业优先调度算法SJF:算法从就绪队列中选择估计时间最短的作业进行处理,直到得出结果或者无法继续执行(周转时间短,但是响应时间长 )

时间片轮转调度RR:按到达的先后对进程放入队列中,然后给队首进程分配CPU时间片,时间片用完之后计时器发出中断,暂停当前进程并将其放到队列尾部,循环 ;(响应时间可以得到保证)

多级反馈队列调度算法:目前公认较好的调度算法;设置多个就绪队列并为每个队列设置不同的优先级,第一个队列优先级最高,其余依次递减。(堆实现优先队列)

五、锁

多线程编程中需要使用的锁。锁要解决的是线程之间争取资源的问题,这个问题大概有下面几个角度:

  • 资源是否是独占(独占锁 - 共享锁)
  • 抢占不到资源怎么办(互斥锁 - 自旋锁)
  • 自己能不能重复抢(重入锁 - 不可重入锁)
  • 竞争读的情况比较多,读可不可以不加锁(读写锁)

内存管理

六、堆栈

栈是用于存放本地变量,内部临时变量以及有关上下文的内存区域。系统操作,不需要程序员手动干预。

栈是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的。eg.递归过深,超过,stackoverflow

七、缓存算法

LRU(least recently used)最近最久未使用

页面置换算法——插入、访问、淘汰。

1、哈希+双向链表:

现在哈希表中查询是否存在该数据,根绝索引找到数据存储的位置,查询时间变为O(1)。

先查询链表中是否有该分页,没有的话查看cache是否满,未满直接放在头尾;满了则删除尾部,再将其放在头部。

存在该分页,则先移除原本的node,将其放在头部。

2、哈希表+数组 :

哈希表存储数据项对应的位置+频次,被访问后频次+1。插入和访问为O(1)

淘汰的时候在表中找到最少访问的,在数组中删除,再放入新的内容,淘汰为O(n)。

操作系统五个基本功能

1、存储管理:内存分配、内存保护、地址映射、内存扩充
2、处理机管理:进程控制、进程同步、进程通信、进程调度
3、设备管理:缓冲管理、设备分配、设备处理、设备独立性和虚拟设备
4、文件管理:外存管理、目录管理、文件操作
5、用户接口:命令接口、程序接口、图形接口

第2部分 进程管理

第1章 进程与线程

1.1 进程

进程(Process):一个具有一定独立功能的可并发执行的程序,在一个数据集合上的运行过程。用来记录进程信息的数据结构

进程控制块PCB:用以记录与进程相关信息的主存区,是进程存在的唯一标志(管理进程的核心,包含了PID等进程的所有关键信息)用链接指针链成队列;

进程的三种基本状态(多线程时也是这些状态)

  • ​ 运行状态(Running)
  • ​ 就绪状态(Ready)
  • ​ 阻塞状态(Block)

队列:就绪队列、等待(阻塞)队列。

  • 处于就绪状态的进程,在调度程序为之分配了处理机之后便开始执行, 就绪 -> 执行
  • 正在执行的进程如果因为分配他的时间片已经用完,而被剥夺处理剂, 执行 -> 就绪
  • 如果因为某种原因致使当前的进程执行受阻,使之不能执行。 执行 -> 阻塞

1.2 线程

线程(Thread),是进程中的一个实体,是能被系统独立调度和分派的基本单位。引入线程,是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。

线程最直接的理解就是“轻量级进程”。

2.3 进程和线程的对比

进程是系统进行资源调度和分配的基本单位;线程是CPU调度的基本单位。

进程 = 资源 (包括寄存器值,PCB,内存映射表)+ TCB(栈结构)
线程 = TCB(栈结构)

线程 的资源是共享的
进程 间的资源是分隔独立的,内存映射表不同,占用物理内存地址是分隔的

线程 的切换只是切换PC,切换了TCB(栈结构)
进程 的切换不仅要切换PC,还包括切换资源,即切换内存映射表

第2章 进程的同步与通信

2.1 进程同步

进程之间存在两种基本关系:竞争关系和协作关系。

同步的解决方案:管程,信号量。

信号量

即利用PV操作来对信号量进行处理。当它的值大于0时,表示当前可用资源的数量;

当它的值小于0时,其绝对值表示等待使用该资源的进程个数。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

2.2 进程的数据通信

1、共享存储器系统

A、共享数据结构:

信息交换的格式、类型一定;

进程通信由程序员完成,效率低;

B、共享存储区:

进程可随时向系统申请一块存储区,

并指定该分区的关键字,用于进程通信。

2、消息传递系统

A、直接通信方式 Send(Receiver,message);Receive(Sender,message);

B、间接通信方式:1、信箱的创建、撤消; 2、消息的发送和接受;

Send(mailbox,message); Receive(mailbox,message);

3、管道通信(Pipe Communication)

管道:用于连接一个读进程和一个写进程,以实现它们之间通信的共享文件,又称为Pipe文件。

功能:Command1进程以字符流的形式向管道发送大量的数据,Command2进程则从管道接收数据。两进程实现单向、同步、互斥运行。

​ 单向:Command1只能发送;

​ 同步:管道满时,Command1等待;

​ 互斥:同一时刻,只能有一个进程对管道操作;

(4)消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

(5)套接字(socket):套接口也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同的主机间的进程通信。通过”套接字”向网络发出请求或者应答网络请求。

2.3 几种线程间的通信机制

1、锁机制

1.1 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。

1.2 读写锁:允许多个线程同时读共享数据,而对写操作互斥。

1.3 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2、信号量机制:包括无名线程信号量与有名线程信号量

3、信号机制:类似于进程间的信号处理。

###

第4章 调度与死锁

CPU调度算法 (在就绪序列中怎么挑选进程让CPU执行)

先了解两个概念:

  • 周转时间: 从开始申请执行任务,到执行任务完成
  • 响应时间: 从开始申请执行任务到开始执行任务

先来先服务调度算法FCFS:按作业或者进程到达的先后顺序依次调度;(平均周转时间可能会很长 )

短作业优先调度算法SJF:算法从就绪队列中选择估计时间最短的作业进行处理,直到得出结果或者无法继续执行(周转时间短,但是响应时间长 )

高相应比算法HRN:响应比=(等待时间+要求服务时间)/要求服务时间;

时间片轮转调度RR:按到达的先后对进程放入队列中,然后给队首进程分配CPU时间片,时间片用完之后计时器发出中断,暂停当前进程并将其放到队列尾部,循环 ;(响应时间可以得到保证)

多级反馈队列调度算法:目前公认较好的调度算法;设置多个就绪队列并为每个队列设置不同的优先级,第一个队列优先级最高,其余依次递减。

✨死锁

  在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。

✨产生条件:互斥、占有等待、非剥夺和循环等待。

  • 互斥条件 :一个资源一次只能被一个进程使用

  • 请求保持条件 :一个进程因请求资源而阻塞时,对已经获得资源保持不放

  • 不可抢占条件 :进程已获得的资源在未使用完之前不能强行剥夺

  • 循环等待条件 :若干进程之间形成一种头尾相接的循环等待资源的关系

✨死锁处理:

破坏产生死锁的4个必要条件中的一个或者多个

  • 预防死锁:预先静态分配法(破坏不可剥夺条件)、资源有序分配法(将资源分类按顺序排列,保证不形成环路)。
  • 避免死锁:银行家算法(对每个资源请求进行检测,确保安全。需要很大的系统开销)。
  • 检测死锁:资源分配图简化,非孤立点的进程处于死锁状态。
  • 解除死锁:资源剥夺法、撤销进程法。

IOS开发 - 简单的计时器

发表于 2019-02-28 | 分类于 IOS开发 | | 阅读次数:
字数统计: 1.3k | 阅读时长 ≈ 6

项目介绍

4

项目完全由代码所写,没有使用storyboard,代码更具有复现性。

手动部分:需要将图片拖入assets

项目知识

SnapKit 布局

经典的Swift版的第三方库,专门用于项目的自动布局,目前在github上的stars就高达9340颗星。

作者仍然是写Objective-C的第三方库Masonry的大牛 - @Robert Payne。

SnapKit配置

1.github下载:https://github.com/SnapKit/SnapKit

2.将下载下来的SnapKit项目的 SnapKit.xcodeproj 拖进自己的项目目录最外层中

3.工程文件 -> General -> Embedded Binaries 中点击加号,添加SnapKit库到项目中来

4.command+B 重新编译项目

SnapKit使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import UIKit
import SnapKit

class ViewController: UIViewController {
// 1.定义box
var box = UIView()

override func viewDidLoad() {
super.viewDidLoad()

box.backgroundColor = UIColor.orange
// 2.将box加入父视图,之后的布局是相对于父视图布局的
self.view.addSubview(box)
// 3. 添加布局的约束:上下左右、中心、
box.snp.makeConstraints { (make) -> Void in
make.width.equalTo(100)
make.height.equalTo(100)
make.center.equalTo(self.view)
}
}
}

Timer 计时器

初始化

1
2
3
4
self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true ,block:{ (timer) in
// withTimeInterval : 的值0.1s 时间片后,执行的代码
// repeats : withTimeInterval的时间后,是否继续重复
})

启动:fire

1
self.timer.fire()

您可以使用此方法来触发重复计时器,而不会中断其常规的触发计划。

如果计时器不重复 repeats,则在触发后自动失效,即使其预定的触发日期尚未到达。

暂停:invalidate(移除计时循环)

1
self.timer.invalidate()

官方的解释是:

This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes and releases the timer, either just before the invalidate method returns or at some later point.

这是唯一一个把一个定时器从NSRunLoop object运行循环中移除的方法。

NSRunLoop object这个对象移除,并且release掉这个的定时器,或者是在这个invalidate方法返回的之前或是在之后的某个时间段,再进行移除并release操作。

guard

guard 打头只判断合理条件,保证判断条件简约

guard xxx : 如果不满足gurad后面的条件xxx,则执行else的内部代码

详见 guard详解

源码

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//
// ViewController.swift
// WatchDemo
//
// Created by iris on 2019/2/27.
// Copyright © 2019 iris. All rights reserved.
//

// 学习:
import UIKit
import SnapKit

class ViewController: UIViewController {

var timer:Timer!
var resultLabel:UILabel! //计时器:文字显示
var resultNumber:Double = 0 //计时器:时间记录

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

// 1.计时器的数字显示
// 1.1 数字的背景
let resultView = UIView()
resultView.backgroundColor = UIColor.init(red: 0.0, green: 0.0, blue: 0.1, alpha: 1)
// 注意:使用snp.makeConstraints方法的元素必须事先添加到父元素的中,例如:self.view.addSubview(view)
self.view.addSubview(resultView)
resultView.snp.makeConstraints{ (make) in
make.top.equalTo(0)
make.left.equalTo(0)
make.height.equalTo(300) // 长度 300
make.width.equalTo(self.view.frame.width)
}
// 1.2 数字的显示
resultLabel = UILabel()
self.view.addSubview(resultLabel)
resultLabel.textColor = UIColor.black
resultView.addSubview(resultLabel)
resultLabel.snp.makeConstraints{ (make) in
make.center.equalTo(resultView)
make.width.height.equalTo(140)
make.width.equalTo(400)
}
resultLabel.font = UIFont.init(name: "Helvetica Neue", size: 60)
resultLabel.text = "0.0"
resultLabel.textColor = UIColor .white
// 设置字体对齐方式
resultLabel.textAlignment = NSTextAlignment.center

// 2.开始按钮
// 2.1 开始按钮:背景
let startView = UIView()
startView.backgroundColor = UIColor.init(red: 0.1, green: 0.0, blue: 0.8, alpha: 0.8)
self.view.addSubview(startView)
startView.snp.makeConstraints { (make) in
make.top.equalTo(300)
make.left.equalTo(0)
make.bottom.equalTo(0)
make.width.equalTo(self.view.frame.width / 2)
}
// 2.2 开始按钮:布局
let startButton:UIButton = UIButton()
// startButton.setTitle("Start", for: UIControl.State.normal)
startButton.setImage(UIImage (imageLiteralResourceName: "start"), for: UIControl.State.normal)
startView.addSubview(startButton)
startButton.snp.makeConstraints { (make) in
make.center.equalTo(startView)
make.width.height.equalTo(50)
}
// 2.3 开始按钮:点击事件添加
startButton.addTarget(self, action: #selector(startHander), for: UIControl.Event.touchUpInside)

// 3 停止按钮
// 3.1 停止:背景
let stopView:UIView = UIView()
stopView.backgroundColor = UIColor.init(red: 0.1, green: 0.8, blue: 0.0, alpha: 0.8)
self.view.addSubview(stopView)
stopView.snp.makeConstraints { (make) in
make.top.equalTo(300)
make.bottom.equalTo(0)
make.right.equalTo(0)
make.width.equalTo(self.view.frame.width / 2)
}
// 3.2 停止:按钮
let stopButton:UIButton = UIButton()
// stopButton.setTitle("Stop", for: UIControl.State.normal)
stopButton.setImage(UIImage (imageLiteralResourceName: "pause"), for: UIControl.State.normal)
stopView.addSubview(stopButton)
stopButton.snp.makeConstraints { (make) in
make.center.equalTo(stopView)
make.width.height.equalTo(50)
}
// 3.2 停止:按钮事件添加
stopButton.addTarget(self, action: #selector(stopHander), for: UIControl.Event.touchUpInside)

// 4 重置
// 4.1 重置按钮
let resetButton:UIButton = UIButton()
resetButton.setTitle("Reset", for: UIControl.State.normal)
resetButton.titleLabel?.font = UIFont.init(name:"Helvetica Neue",size:20)
resultView.addSubview(resetButton)
resetButton.snp.makeConstraints { (make) in
make.right.equalTo(-10)
make.top.equalTo(10)
make.width.equalTo(100)
make.height.equalTo(50)
}
// 4.1 重置:事件监听
resetButton.addTarget(self, action: #selector(resetHander), for: UIControl.Event.touchUpInside)

}

// 3个按钮的事件函数
// 1.开始计时
@objc func startHander(){
if self.timer != nil{
self.stopHander()
}
self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true,block:{ (timer) in
self.resultNumber = self.resultNumber + 0.1
self.resultLabel.text = String(format:"%.1f",self.resultNumber)
})
self.timer.fire()
}
// 2.暂停时间
@objc func stopHander(){
// 2.1 不进行判断的方法,可能出现异常,内存可能泄露
// var timerForDistory:Timer!
// timerForDistory = self.timer
// timerForDistory.invalidate()

// 2.2 进行判断的方法,可能出现异常,内存可能泄露
// guard 打头只判断合理条件,保证判断条件简约
// guard xxx : 如果不满足gurad后面的条件xxx,则执行else的内部代码
guard let timerForDistory = self.timer else {
return
}
timerForDistory.invalidate()
}
// 3.重置计时器
@objc func resetHander(){
self.resultNumber = 0
self.resultLabel.text = "0.0"
}
}

IOS开发 - 简单的加法计算器swift版本

发表于 2019-02-26 | 分类于 IOS开发 | | 阅读次数:
字数统计: 727 | 阅读时长 ≈ 3

近日开始学习IOS开发。想找一个简单版本较新的IOS开发教程都很难:(1)现在找的很多都是object-C的,不是swift的,(2)老版与最新版本swift中很多语法也不适配,(3)Xcode版本和用法也不一样。

因此,今天自己写一个最简单的教程,用于swift入门,记录自己的学习。

基础步骤

基础步骤就是项目类型、项目名称、项目位置的选取。

1.创建APP

1061D097-18CB-43D0-A701-5DF5918FB88E

阅读全文 »

Mac系统下使用Hexo在Github上搭建博客

发表于 2019-02-23 | 分类于 技术 | | 阅读次数:
字数统计: 17 | 阅读时长 ≈ 1

一、安装环节

二、Hexo部署

三、github仓库

四、git代码

工具整理 - 设计类工具

发表于 2019-02-19 | 分类于 实用工具 | | 阅读次数:
字数统计: 64 | 阅读时长 ≈ 1

设计icon:https://www.canva.cn/

生成icon:https://appicon.co/

海报:

阿里矢量图标:https://www.iconfont.cn/

矢量图标:https://www.easyicon.net/update/

机器学习:https://mp.weixin.qq.com/s/F_AvAtITyQHWhXgi2K02Ag

阅读全文 »

Node.js的Web服务器搭建

发表于 2019-02-19 | 分类于 前端 | | 阅读次数:
字数统计: 70 | 阅读时长 ≈ 1

服务器搭建

1
2
3
4
5
//安装服务区
npm install http-server -g

//启动服务器
http-server
阅读全文 »

暑期实习面试 - 腾讯PCG移动客户端iOS开发面试

发表于 2019-02-11 | 分类于 面试 | | 阅读次数:
字数统计: 1.2k | 阅读时长 ≈ 4

基本信息

事业群:PCG

产品:手机QQ 看点

岗位:移动客户端开发(iOS、Objective-C语言开发)

地点:深圳总部

转正:几率较小

实习时间:6月份之后的暑期实习

面试日期:3月30日 - 3月31日

offer call:4月10日(看到很多小伙伴都接到offer call了,内心有一些着急,9号换了ycy头像)

阅读全文 »

简历 - 简历要点

发表于 2019-01-19 | 分类于 简历 | | 阅读次数:
字数统计: 812 | 阅读时长 ≈ 2

简历命名

除了姓名是一定要有的,最好还有这几个信息:岗位方向、学校、几几届(不是几几级),后两个是针对校招简历

阅读全文 »

IOS12+Swift4+Xcode10开发 - 4 元气天气APP

发表于 2019-01-06 | 分类于 APP | | 阅读次数:
字数统计: 4.2k | 阅读时长 ≈ 16

项目介绍

app6的副本

需求分析

iOS原生风格的极简的日程管理工具。

现有的Todo工具:UI设计趋向于小清新、可爱动画等多元设计;功能为可以记录时间、地点、时间重要程度等信息,这样对于提高了增加任务的时间、精力成本。

本APP主打:界面使用iOS原生风格,随时想到、随时记录。

功能:用户可以对待办事项输入、修改、删除、修改顺序操作,可以标记已经完成任务。

使用场景:午餐时想到了一个idea直接打开APP记录、午睡后想到这个idea的实现可直接修改,晚上实现该功能后直接打钩,第二天测试人员告知该idea可行时删除任务。

功能需求

浏览任务页面:可以在tableview查看所有任务及其完成状态;可以在编辑状态下批量删除任务、移动任务顺序;按下navigation的+进入添加任务页面;按下>进入修改任务页面。

添加修改页面:判断当前任务是添加还是修改。使用自定义protocol和delegate反向传值,将文字传回浏览页面。

知识点

主线程

https://www.jianshu.com/p/f042432e2d7d

TableView的使用

1.我们知道tableView是IOS中的高级视图,其继承与ScrollView,具有ScrollView的功能,还扩展了许多。

2.tableView的表现格式分两种Plain和Grouped两种风格

3.tableView的两种代理类delegate和dataSource.这两种代理至关重要,我们在做tableView和这些代理是分不开的。

4.代理中比较常用的代理方法:

(1)dataSource的两个必须使用的代理

显示UITableView的Cell的个数:一共有多少个格子

Cell和model的数据的交互:根据数据,获取每一个数据的值

5.增删改查需要做的:第一步获取需要操作的行数(增加不需要),第二步对数据库进行处理,第三部更新视图

6.更新视图的2种方式:beginUpdates、endUpdates之前操作(如一行一行的删除数据);tableView.reloadData()刷新TableView了。

在使用数据库之后,我就直接reloadData()刷新TableView了。比较方便

1
2
3
4
5
6
// 方式一:把批量对视图的操作防在两句话中间,可以提高app的性能
tableView.beginUpdates()
// codes
tableView.endUpdates()
// 方式二:从新load数据更新页面;但没有动画效果 —— 就是相当于执行 cell for row的方法,将结果取出来,再更新视图
//tableView.reloadData()

Navigation使用,sugue实现跳转

常用控件:相当于一个扑克盒子,里面的页面是出栈和入栈来实现试图切换。

利用segue界面跳转一共有两种方式:

第一种就是以上我的例子,利用button组件,拖拽添加segue,直接运行就可以用。

第二种是利用ViewController与ViewController之间,拖拽添加segue。不过,这种方法就需要在相应需要跳转的方法内写入代码,手动去设置它的跳转。

iOS 10中有几种sugue类型:

Show:新的view controller将被添加view controller栈的顶部。跳转的页面有Navigation bar,并且有返回原来页面的返回按钮。这是非常常用的的类型。

Show detail:在view controller栈中,新的view controller将被替代原来的view controller。跳转的页面没有Navigation bar,也没有返回原来页面的返回按钮。

Present modally:新页面将以动画形式从底部出现到覆盖整个手机屏幕。这种形式最常见的列子是iOS自带的日历应用:

阅读全文 »

iOS基础

发表于 2019-01-06 | 分类于 iOS开发 | | 阅读次数:
字数统计: 3.7k | 阅读时长 ≈ 14

RunLoop

runloop是一个事件循环对象。

do {

//接受消息->等待->处理

}while(message != quit)

  • 概念:事件循环对象,在循环过程中处理各种事件(点击、刷新等),从而保持程序持续运行;在没有事件处理的时候,会进入睡眠模式,从而节省CPU资源,提高程序性能。
  • 为什么需要:一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制。Runloop是事件接收和分发机制的一个实现。
  • Runloop 和线程是绑定在一起的。每个线程(包括主线程)都有一个对应的 Runloop 对象。我们并不能自己创建 Runloop 对象,但是可以获取到系统提供的 Runloop 对象。

是否接触TableView渲染性能相关的东西?

  • cell的数目,配置tableview数据

    重用单元格的形式,数据成千上万行,最终渲染个数为屏幕上显示的数目。

    往下拉的时候,最上面的cell到最下面来,放置重复渲染,提高手机性能。

  • 刷新页面的两种方法

    • 无动画效果:tableView.reloadData(),就是相当于执行 cell for row的方法,将结果取出来,再更新视图
    • 将刷新语句放在View.beginUpdates()、tableView.endUpdates()中间,可以提高app的性能

事件处理

触摸屏幕、晃动设备、通过遥控设施控制设备。对应的事件类型有以下三种:

  1. 触屏事件(Touch Event)
  2. 运动事件(Motion Event)
  3. 远端控制事件(Remote-Control Event)

响应者链

当发生事件响应时,必须知道由谁来响应事件。在 iOS 中,由响应者链来对事件进行响应。

所有事件响应的类都是 UIResponder 的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获得响应事件消息的机会。

1
First Responser --> The Window --> The Application --> nil(丢弃)

我们可以通过 [responder nextResponder] 找到当前 responder 的下一个 responder,持续这个过程到最后会找到 UIApplication 对象。

通常情况下,我们在 First Responder (一般也就是用户当前触控的 View )这里就会响应请求,进入下面的事件分发机制。

事件分发

第一响应者(First responder)指的是当前接受触摸的响应者对象(通常是一个 UIView 对象),即表示当前该对象正在与用户交互,它是响应者链的开端。响应者链和事件分发的使命都是找出第一响应者。

  • iOS 系统检测到手指触摸 (Touch) 操作时会将其打包成一个 UIEvent 对象,并放入当前活动 Application 的事件队列,
  • 单例的 UIApplication 会从事件队列中取出触摸事件并传递给单例的 UIWindow 来处理,
  • UIWindow 对象首先会使用 hitTest:withEvent:方法寻找此次 Touch 操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为 hit-test view。

内存管理机制:ARC自动引用计数\MRC

创建的时候为1,使用的时候+1,不需要的时候-1,为0的时候系统知道其不需要了,则release。

class和struct

相同点

  • 可以定义存储属性
  • 能够定义方法
  • 通过下标操作访问实例所包含的值
  • 通过扩展来增加默认实现功能

不同点

  • 类可以继承
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 析构器允许一个类实例释放任何它所被分配的资源
  • 引用计数允许一个类的多次引用
  • class是引用类型,struct是值类型
  • struct会自动生成一个初始化所有属性的构造器

多线程

谈及 iOS 中的多线程,一般说的是 pthread,NSthread,GCD,NSOperation 这四种, 用的最多也最方便的就是 GCD 了。

四、线程安全问题

当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。就好比几个人在同一时修改同一个表格,造成数据的错乱。

解决多线程安全问题的方法

  • 方法一:互斥锁(同步锁)
1
`@synchronized(锁对象) {``    ``// 需要锁定的代码``}`

加了互斥做的代码,当新线程访问时,如果发现其他线程正在执行锁定的代码,新线程就会进入休眠。

  • 方法二:自旋锁

加了自旋锁,当新线程访问代码时,如果发现有其他线程正在锁定代码,新线程会用死循环的方式,一直等待锁定的代码执行完成。相当于不停尝试执行代码,比较消耗性能。

3873004-d66485790959dcf5.png

GCD 中两个重要重要概念 —— 队列 & 任务

队列是一种特殊的线性表,采用FIFO(先进先出)的原则,队列的主要作用是用来存放任务。

GCD会自动将队列中的任务取出,放到对应的线程中执行。

串行队列(Serial Dispatch Queue): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务

并发队列(Concurrent Dispatch Queue): 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务), 并发功能只有在异步(dispatch_async)函数下才有效

队列执行任务的方式:

  • 同步:在当前线程中执行,当前代码不执行完,就不能够执行下一条代码。会阻塞当前线程。
  • 异步:在另一条线程中执行(不用等待当前代码执行完,就能够执行下一条),不会阻塞当前线程。

  • 主队列 (串行)

1
let mainQueue = DispatchQueue.main
  • 全局队列 (并发)
1
let globalQueue = DispatchQueue.global()

全局队列默认是并发队列,在不进行第三方框架或者大型的商业应用开发,全局队列基本够用。

  • 主队列 dispatch_main_queue(); 串行 ,更新UI
  • 全局队列 dispatch_global_queue(); 并行,四个优先级: background,low,default,high
  • 自定义队列 dispatch_queue_t queue; 可以自定义是并行: DISPATCH_QUEUE_CONCURRENT 或者串行 DISPATCH_QUEUE_SERIAL

同步不开异步开,串行开1条,并行开多条。

队列中的任务同步执行,队列就不具有开启线程的能力, 队列中的任务异步执行,队列就具有开启线程的能力。
(同步和异步执行决定的是否有开启线程的能力)

如果队列具 有开启线程的能力 (队列任务异步执行) 且队列是 串行队列 ,那么将会 开启 1 条线程 。
如果队列具 有开启线程的能力 (队列任务异步执行) 且队列是 并发队列 ,那么将会 开启 多 条线程 。开启线程的条数由 GCD 决定。

2.1 全局队列

全局队列是获取的,不是程序员创建的。
为了方便 GCD 的使用,apple 默认为我们提供的。
全局队列默认是并发队列,在不是进行第三方框架或者大型的商业应用开发,全局队列基本够用。

全局 ( 并发 ) 队列异步执行 :

并发队列异步(不阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启多条线程。

1
2
3
4
5
6
7
8
任务异步执行不会阻塞当前线程, 
befor 在最前,
after 在任意位置,
task 执行顺序不确定 —— 并发执行(index可以确认)。
task 并发执行 —— 并发执行(number可以确认)。

异步开线程 number 可以确定开启了多条线程
开的线程数由 GCD 决定。 可以看到线程的 number 有重复,是 GCD 对线程进行了复用。
1
2
3
4
5
6
7
8
9
10
11
12
func async() {
print("DispatchQueue.global().async: befor", Thread.current)
// 全局队列进行 10次异步
for index in 0..<10 {
DispatchQueue.global().async {
print("DispatchQueue.global().async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue.global().async: after", Thread.current)
}

打印:

全局 ( 并发 ) 队列同步执行 :

并发队列同步(阻塞当前线程)执行(队列就不具有开启线程的能力), 队列不会开启线程(代码都在主线程中执行)。

1
2
3
4
5
任务同步执行会阻塞当前线程, 
befor 在最前,
after 在最后,
task 执行顺序确定 —— 阻塞。
同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func sync() {
print("DispatchQueue.global().sync: befor", Thread.current)
for index in 0..<10 {
DispatchQueue.global().sync {
print("DispatchQueue.global().sync: task:(taskIndex:\(index))", Thread.current)
}
}
print("DispatchQueue.global().sync: after", Thread.current)
}


打印:
DispatchQueue.global().sync: befor <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:0) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:1) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:2) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:3) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:4) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:5) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:6) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:7) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:8) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:9) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: after <NSThread: 0x60800007fa80>{number = 1, name = main}

2.2 主队列

主队列是获取的,不是程序员创建的,apple 默认为我们提供的。
(app 开发中,所有的 UI 更新操作都应该在主线程中进行)

主队列(串行)异步执行

主队列异步(不会阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启线程(开启的线程就是主线程)。

1
2
3
4
5
6
7
8
9
10
11
> 有朋友问我,异步会开启线程, 主队列异步就不会开启线程。 
> 我当时还信以为真。认为自己错误,说特殊情况特殊处理。 其实说白了就是学艺不精。
> 由于主队列是我们获取的,不是我们创建的,在某种意识中会认为主线程不是在主队列中创建的。(认为一开始就存在的。)


> 任务异步执行不会阻塞当前线程,
befor 在最前,
after 在第二,
task 执行顺序确定 —— 串行执行(index可以确认)。

同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。

主队列异步的操作主要用在更新 UI 操作中。 具体参考 项目开发中 GCD 代码使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func async() {

print("DispatchQueue.main.async: befor", Thread.current)
for index in 0..<10 {
DispatchQueue.main.async {
print("DispatchQueue.main.async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue.main.async: after", Thread.current)
}

打印:
DispatchQueue.main.async: befor <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: after <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:0 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:1 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:2 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:3 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:4 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:5 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:6 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:7 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:8 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:9 <NSThread: 0x60800006ddc0>{number = 1, name = main}

主队列(串行)同步执行

执行的效果就俩字 死锁

主线程同步,在 Swift 中,编译阶段就报错,在 oc 中是在运行的时候才能发现。体现的主要是界面的 “假死”。

UITableView

要实现Tableview必须要签订2个协议

UITableViewDelegate:数据视图的普通协议,作用是:处理数据视图事件.
UITableViewDataSource:数据视图的数据代理协议,作用是:处理视图的数据代理.

实现签订的协议的2个协议方法:
返回数字,每个section有多少个row

返回cell,cell就是TableView显示时候的格子内容,根据indexPath和数据决定;

观察者设计模式、KVO

设计模式——观察者设计模式

A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化。

指定一个被观察对象(例如 A 类),当对象某个属性(例如 A 中的字符串 name)发生更改时,对象会获得通知,并作出相应处理;

在 MVC 设计架构下的项目,KVO 机制很适合实现 mode 模型和 view 视图之间的通讯。比如label中的对象为 num=0 ,我们按下按钮之后对象的num++,则label改变。

设计模式——通知机制

当用户从后台进入,从非活跃的状态进入活跃状态的时候,系统自动发送“becomeActive”通知,如果有注册监听者(观察者),则执行回调方法。【天气预报需要用】

网络

浏览器进行一次网络请求都需要哪些步骤?

  • 1、域名解析:
    • 浏览器查找域名的 IP 地址、
    • 没找到后发送给 DNS 服务器(本地host,电信),让它解析为 IP 地址
  • 2、TCP的三次握手
  • 3、建立TCP连接后发起HTTP请求,浏览器向 web 服务器发送一个 HTTP 请求
    • HTTP请求格式:是请求方法(GET/POST/DELETE/PUT/HEAD)、URI路径、HTTP版本号。
    • 3. 2 服务器响应HTTP请求:状态行、响应头、空行、消息体。
  • 4、浏览器解析html代码,并请求html代码中的资源
    • 服务器的永久重定向响应:“https://www.google.com/” 而非“http://google.com/”。

三次握手

第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。

1…345…7
IRIS

IRIS

64 日志
19 分类
22 标签
GitHub E-Mail
0%
© 2019 IRIS | Site words total count: 72.2k