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

git reset --soft HEAD~N 回退到上N个版本
git reset --soft head^ 将最近一次提交记录回退到暂存区
Git reset --mixed head^ 将最近一次提交回退到工作区
git reset --hard head^ 将最近一次提交记录清除
git reset --hard commitId 回退版本

git cherry-pick commitid 把其他分支的某提交记录复制到当前分支
git cherry-pick --abort 取消cherry-pick
git merge --abort 取消合并
git checkout . 重置当前所有修改(危险)

git revert (用一次新的提交回滚之前的提交且用于非合并类型的提交,而get reset 是直接删除指定的commit)
git revert head 撤销前一次提交
git revert -n commitid 撤销指定提交记录

git checkout -b test origin/test 本地创建一个和远程同名的分支
git pull origin test 更新test这个分支的代码
git checkout test 切换到test分支
git branch -a 查看远程分支
git branch -vv 查看分支关联
git status 查看当前状态
git log 查看提交日志

git pull 的意思是 git fetch 再 git merge FETCH_HEAD
git pull --rebase 的意思是 先 git fetch 再 git rebase FETCH_HEAD
git pull --autostash --rebase
git rebase -i 将本地的多次提交合并为一个(简化提交记录,合并到develop的时候看到的提交记录更加清爽)
git rebase -i HEAD~2 //合并前两个提交记录
git rebase 也可以用于合并。例如在开发分支执行git rebase develop,有冲突就解决冲突,解决后直接git add . 再git rebase --continue 即可。(git rebase 与 git merge 的原理不同,合并后并不会产生新的提交记录。)。 功能开发完成最后切到develop分支把功能进行合并进来 git merge xxx分支名。

git branch —delete branchName 删除本地分支
git push origin --delete test 删除远程分支

git tag 1.0.2 打tag
git push origin --tags 推送tag到远程
git push origin test1:test1 关联分支到远程分支

wechatSDK用在分享,支付等场景。 1.8.6 以上采取unverslink唤起app,当跳转失效时可以打开sdk日志开关来查看问题原因。

Xcode需要配置Associated Domains , 配置访问的域名。for example:
applinks:h5-vendor-prod.gaoying.com

Xcode 需要配置 apple-app-site-association 文件,该文件无后缀,里面是 json 配置。

1
2
3
4
5
6
7
8
9
10
11
{
"applinks": {
"apps": [],
"details": [
{
"appID": "yourDeveloperTeamId.com.livehome.com",
"paths": ["/livehome/*"]
}
]
}
}

wechatSDK 注册

1
2
3
4
5
6
7
let wechatFlag = WXApi.registerApp("wechatAppId", universalLink: "https://h5-vendor-prod.gaoying.com/livehome/")
NSLog("微信flag:\(wechatFlag)")
WXApi.startLog(by: .detail) { (logStr) in
NSLog("微信日志:\(logStr)")
}
let versionStr = WXApi.getVersion()
NSLog("微信sdk版本号:\(versionStr)")

需要到微信开放平台上设置 universLink。需要注意的是url必须要是https。

多Target场景配置:
iOS Project 多个 Target UniversLink 配置(付费文章)

谈到属性的原子性,即操作读写的原子性(安全的,单一的,完整的) 可以这么理解。那有什么方法可以做到?自然,我们想到了锁的概念。

锁的种类

我们在开发Apps的时候,经常会用到多线程。而锁的概念可以抽象成线程同步。主要作用即保护授权某段代码块的原子性执行。iOS中锁有很多种类:

  • 信号量 Semaphore (允许N个有限线程在某一时间执行指定代码块)
  • 线程锁 Mutex 保证在某一时间内执行某代码块只有一个线程进入。
  • 自璇锁 Spinlock 指的是一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到的情况下会循环等待,不断的判断。
  • 读写锁 Read-write-lock 并发只读,串行写。
  • 递归锁 Recursive-lock 这个锁可以被同一线程多次请求,而不会引起死锁。它主要是用在循环或递归操作中。

Grand Central Dispatch (GCD),CPU多核处理,线程间异步高效地执行任务。在GCD的管理下,只需提供DispatchWorkItem作为单个任务,本质上是个swift 闭包。这些任务项item由GCD根据一定地规则排队自动执行。通过 queue.async(execute: workItem)。通过GCD可设置任务的优先级,以及执行顺序。
几个任务项也可以作为一个group, 通过DispatchGroup组织多任务执行,最后将所有处理完毕的结果作为单一整体汇总。
队列Queue管理着任务项的执行,可串行也可并发。串行队列一次执行一个任务,并发队列无需等待执行中的任务结束才开启下一个任务(即可同时执行多项任务)。两种队列都按照先进先出(FOFO)执行。
内部,有GCD线程池服务所有的队列。当某任务执行完成后线程即销毁。当所有的线程属于busy状态,新的线程需要暂时挂起。GCD预备了五种队列可直接使用。一种串行队列:主队列。四种异步队列分别权限为:hign,default,low,background。

首先我们带着几点疑问去看文章:

  • 值类型(结构体、枚举) 和 引用类型 class 在内存中是如何存储的?
  • 值类型和引用类型性能上有什么不同?
  • 如果两者混合,会发生什么?比如struct 包含了 class 对象。
  • 到底该使用哪个呢?

值类型的定义

  • 值类型直接存储到栈中。每个值类型的变量持有自己的数据,之间互不影响。
  • 引用类型通过指针引用数据(存储在堆中),多个变量可指向同一数据,当操作某一变量时会影响其他变量。

值类型涉及到值拷贝,引用类型涉及到内存分配,引用计数。接下来我们大致了解一下内存段。

UserDefaults可以存储一些基础数据类型,如:Data,String,Date,Bool,Int,Double,Float,Array,Dictionary,URL,等基础对象。也可以存储自定义对象(但需要实现编码成Data进行存储)。
尽管如此,我们还是不推荐使用UserDefaults来存储数据量大的数据。因为读写非常昂贵,userDefaults最终用.plist文件进行存储。存储大数据会使此文件变得臃肿。
对于自定义的对象,我们可以通过写文件方式将json写到沙盒文件中。或者存储到coreData. Sqlite等。UserDefaults一般用于存储一些简单的数据类型,存储用户的偏好设置等。在写少读多的情况下,性能才会高。底层是通过读写xml文件方式都数据进行读写。

SUT

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
extension URLRequest {
static func search(term: String) -> URLRequest {
var components = URLComponents(string: "https://itunes.apple.com/search")
components?.queryItems = [
.init(name: "media", value: "music"),
.init(name: "entity", value: "song"),
.init(name: "term", value: "\(term)")
]

return URLRequest(url: components!.url!)
}
}

struct MusicService {

func search(_ term: String, completion: @escaping (Result<[Track], Error>) -> Void) {
URLSession.shared.dataTask(with: .search(term: term)) { data, response, error in
DispatchQueue.main.async {
completion(self.parse(data: data, error: error))
}
}.resume()
}

func parse(data: Data?, error: Error?) -> Result<[Track], Error> {
if let data = data {
return Result { try JSONDecoder().decode(SearchMediaResponse.self, from: data).results }
} else {
return .failure(error ?? URLError(.badServerResponse))
}
}
}

视图测试关注点:

  • tableview的行数是否正确?
  • label的文本显示是否正确?
  • button 是否启用或禁用?
  • view的frame是否正确?

viewController如果可以从他们依赖项隔离出来,那么就可以测试。依赖注入是一种可以把视图控制器隔离出来的技术。在测试中,我们可以用假数据替换依赖项,模拟真实数据的行为。
viewController有两个职责:渲染数据、响应用户交互。在MVVM设计模式中,控制器不主动从model中拉数据,也不负责从model中取出数据来更新控制器。

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
在MVC 框架中,其中C(控制器)就是 M(模型)和 V(视图)的中介者。我们接下来用一个搜索视图来展示中介者模式:

有限状态自动机(FSM “finite state machine” 或者FSA “finite state automaton” )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。
我们来看以下片段代码:

1
2
3
4
5
6
7
class ViewController {
@IBOutlet var tableView: UITableView!
@IBOutlet var errorLabel: UILabel!
@IBOutlet var emptyStateLabel: UILabel!
@IBOutlet var activityIndicator: UIActivityIndicatorView!
// Some implementation
}

我们得出4种状态,且每种独立存在:

  • 显示数据
  • 是否在加载数据中
  • 是否显示错误
  • 空状态
    我们在控制器中定义该枚举:
    1
    2
    3
    4
    5
    6
    7
    8
    extension ViewController {
    enum State {
    case loading
    case showingData([Item])
    case empty
    case error(Error)
    }
    }

Swift能实现多继承吗?

虽然多继承在C++中可以实现,但在Swift中,是不能够实现多继承的。但一个class可以遵循多个协议,只能继承一个父类。但值类型比如 struct , enum 就不存在继承这么一说,但还是可以遵循多个协议的。
Swift只支持协议多继承
如:

1
2
3
4
5
6
7
protocol ChildAB:ChildA,ChildB {

}
class MyClass: ChildAB {
func method() {
}
}