Hi, this is Alice Home


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 公益404

Swift (二) swift 中的原型模式

发表于 2018-07-29 | 分类于 Swift

代码链接

继承自 NSObject 类

可以看到直接调用 copy 方法是可以的,但是程序会报错,这是以为 copy 是 NSObject 的方法,但是 NSObject 类并没有实现 NSCopying 协议,所以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person: NSObject, NSCopying {

var name: String?
func copy(with zone: NSZone? = nil) -> Any {
let newP = Person()
newP.name = name
return newP
}
}

let p = Person()
let p2 = p.copy() as! Person
if let name = p2.name {
print(name)
}

非继承自 NSObject

而向 Dog 却并没有继承自 NSObject,所以程序运行的时候回报出没有 copy 成员的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Dog: NSCopying {
var name: String?
var ower: Person?

func copy(with zone: NSZone? = nil) -> Any {
let copyDog = Dog()
copyDog.name = name
copyDog.ower = ower
return copyDog
}
}

let dog = Dog()
dog.name = "Tom"
let newDog = dog.copy() as! Dog
dog.name

继承类实现原型模式

这里出现了一个问题,就是 TianYuanDog 类型转换不成功,这是因为在 Dog 的 copy(with zone:) 方法中,我们是生成的 Dog 类,所以这里需要做一下修改

修改前:

1
2
3
4
5
6
func copy(with zone: NSZone? = nil) -> Any {
let copyDog = Dog()
copyDog.name = name
copyDog.ower = ower
return copyDog
}

let copyDog = Dog() 这里不能直接生成 Dog 对象,否则子类继承的话会出现问题

1
2
3
4
5
6
7
8
9
10
11
12
    func copy(with zone: NSZone? = nil) -> Any {
// let copyDog = Dog()
let dynamicType = type(of: self)
let copyDog = dynamicType.init()
copyDog.name = name
copyDog.ower = ower
return copyDog
}

required init() {

}

同样的,上面 Person 也要做修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person: NSObject, NSCopying {

var name: String?
func copy(with zone: NSZone? = nil) -> Any {
// let newP = Person()
let personType = type(of: self)
let newP = personType.init()

newP.name = name
return newP
}

required override init() {
}
}

继承类实现原型模式, 并添加新的属性

接下来 TianYuanDog 类 需要添加一个 ID 号,这个 ID 号在实例执行 copy 的时候也需要跟随一起 copy 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class TianYuanDog: Dog {

var idNumber: Int?

required init() {}

init(number idNumber: Int) {
self.idNumber = idNumber
}

override func copy(with zone: NSZone?) -> Any {
let newCopy = super.copy(with: zone) as! TianYuanDog
newCopy.idNumber = self.idNumber
return newCopy
}
}

let tianyuan = TianYuanDog(number:123)
tianyuan.name = "中华田园犬"
let newCopy = tianyuan.copy(with: nil) as! TianYuanDog
newCopy.name
newCopy.idNumber

具体操作:

  1. 重写 copy(with:) 方法
  2. 如果需要添加 init() 方法,需要重写 require 的 init 方法

设计模式(一) 6 个原则

发表于 2018-07-27 | 分类于 Design Pattern

总原则:开闭原则

对扩展开放,对修改关闭

单一原则

不存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如果不能,就应该把类进一步拆分

里氏替换原则

任何基类出现的地方,子类一定可以出现。 LSP 是继承复用的基石,只有当衍生类可以替换掉基类,软件单位功能不受到影响,基类才能真正被复用,而衍生类才能在基类的基础上增加新的行为。

依赖倒转原则

这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

接口隔离原则

每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个接口的隔离,比使用单个接口要好。

迪米特法则

一个类对自己依赖的类知道的越少越好,也就是说无论被依赖的类多么复杂,都应该讲逻辑封装在方法内部,通过 public 方法提供给外部,这样当被依赖的类发生变化的时候,才能最小的影响该类。

合成复用原则

原则尽量首先使用合成/聚合的方法,而不是继承

23 三种设计模式分类:

对象创建模式:

  1. 原型模式
  2. 工厂模式
  3. 抽象工厂模式
  4. 生成器模式
  5. 单例模式

参考:

  1. 23种设计模式全解析

Data Structure(一) Linked List

发表于 2018-07-26 | 分类于 Data Structure

代码地址

链表

链表是数据结构中经常会用到的一种数据结构,是指一串数据项,每一个数据项称之为节点(Node)。

链表主要有两种:

单链表,链表中的每个节点只有向后的索引

双链表,链表除了向后的索引之外,还有指向前一个的索引

定义节点

链表是有节点组成的,首先定义节点的数据结构: Node

  1. 首先将节点定义为一个类 - Node
  2. 添加值,并添加一个初始化方法
  3. 要有一个指针 next 指向下一个节点,注意这个指针必须是可选类型,因为链表最后的一个 Node 的 next 指针不指向任何 Node
1
2
3
4
5
6
7
public class Node {
var val: Int
var next: Node?
init(_ val: Int) {
self.val = val
}
}

如果是双向链表,还需要定义 previous 指针:

1
2
3
4
5
6
7
8
9
10
public class Node {

var value: Int
var next: Node?
weak var previous: Node?

public init(_ value: Int) {
self.value = value
}
}

这里 previous 使用 weak 是为了防止循环引用

定义链表

定义链表,有两个节点 head 和 tail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

//链表
public class LinkedList {

fileprivate var head: Node?
private var tail: Node?

public var first: Node? {
return head
}

public var last: Node? {
return tail
}
}

空链表

1
2
3
public var isEmpty: Bool {
return head == nil
}

添加测试代码:

1
2
3
4
5
6
7
8
9
//测试
testList()

func testLinkedList () {
let link = LinkedList()
assert(link.isEmpty, "空链表")
}

testLinkedList()

添加节点

1
2
3
4
5
6
7
8
9
10
11
12
13

public func append(_ value: Int) {
let newNode = Node(value)

if let tailNode = tail {
newNode.previous = tailNode
tailNode.next = newNode
}
else {
head = newNode
}
tail = newNode
}

说明:

  • 首先根据传入的 value, 创建一个新的节点
  • 如果 tail 不为 nil,就说明当前的链表内有内容,所以执行插入操作
  • 插入操作:newNode 的 previous 指向 tail,tail 的 next 指针指向 newNode,并且设置 newNode 为新的 tail
  • 如果 tail 为 nil, 就说明当前链表为空链表,直接设置 head 为 newNode 就可以了,然后设置 tail 为 newNode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//测试
func testLinkedListAppend() {

let link = LinkedList()
link.append(10)
assert(!link.isEmpty, "非空链表")

assert(link.first?.value == 10, "head 节点为 10")
assert(link.last?.value == 10, "tail 节点为 10")
link.append(20)
assert(link.last?.value == 20, "tail 节点为 20")
link.append(30)
assert(link.last?.value == 30, "tail 节点为 30")
}

testLinkedListAppend()

输出链表

当我们学会插入节点之后,第一时间就行看看链表中的内容。目前如果只是简单的 print(link) 的话是不能打印出处链表的信息的,为了实现输出链表信息,做如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

extension LinkedList: CustomStringConvertible {
public var description: String {

var text = "["
var node = head

while node != nil {
text += "\(node!.value)"
node = node!.next
if node != nil { text += " -> " }
}

return text + "]"
}
}

说明:

  • 首先为 LinkedList 声明 extension,然后实现 CustomStringConvertible 协议,这个协议要求你实现 computed property description:String
  • 所以在 extension 内部,声明可计算属性 description,类型为 String,并且该属性是只读属性
  • 然后在内容通过对 linkedList 的遍历以及字符串拼接,返回需要输出的链表格式

测试:

1
2
3
4
5
6
7
let link1 = LinkedList()
link1.append(1)
link1.append(2)
link1.append(3)
link1.append(4)

print(link1) // output: [1 -> 2 -> 3 -> 4]

playground 自动显示

查找节点

根据输入链表中 node 的位置返回 node object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public func nodeAt(_ index: Int) -> Node? {

if index >= 0 {

var node = head
var i = index

while node != nil {
if i == 0 {return node}
i = i - 1
node = node!.next
}
}
return nil
}

说明:

  • 首先剔除 index 为负数的情况,返回 nil
  • 接着通过 node != nil 来进行循环,然后通过 index 自减是否为零来进行判断

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func testNodeAt () {
let link = LinkedList()
link.append(0)
link.append(1)
link.append(2)
link.append(3)

assert(link.nodeAt(0)?.value == 0, "node[0] = 0")
assert(link.nodeAt(1)?.value == 1, "node[1] = 1")
assert(link.nodeAt(3)?.value == 3, "node[3] = 3")
assert(link.nodeAt(8) == nil , "找不到")
assert(link.nodeAt(-1) == nil , "找不到")
}

testNodeAt()

删除所有节点

1
2
3
4
public func removeAll() {
head = nil
tail = nil
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func testRemoveAll () {

let link = LinkedList()
link.append(0)
link.append(1)
link.append(2)
link.append(3)

link.removeAll()

assert(link.isEmpty, "空")
assert(link.first == nil, "first")
assert(link.last == nil, "last")
assert(link.nodeAt(Int(arc4random())) == nil , "anynoe")

}

testRemoveAll()

删除指定节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public func remove(_ node: Node) -> Int {
let prev = node.previous
let next = node.next

if let prev = prev {
prev.next = next
}else {
//如果删除的是 first
head = next
}

next?.previous = prev
//如果删除的是 last
if next == nil {
tail = prev
}

node.previous = nil
node.next = nil

return node.value
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func testRemove () {

let link = LinkedList()
link.append(0)
link.append(1)
link.append(2)
link.append(3)

let value = link.remove(link.nodeAt(2)!)
assert(value == 2,"删除")
assert(link.last?.value == 3,"删除")
link.remove(link.last!)
assert(link.last?.value == 1, "删除最后一个node,最后一个node 变为 1")
link.remove(link.first!)
assert(link.last?.value == 1, "删除第一个node,第一个node 变为 1")

}

testRemove()

参考:

  1. Swift Algorithm Club: Swift Linked List Data Structure

Swift (一)

发表于 2018-07-26 | 分类于 Swift

Swift 访问控制

private

filePrivate

internal

public

open

iOS 离屏渲染

发表于 2018-07-25

什么是离屏渲染

iOS 的屏幕渲染是通过 GPU 来进行操作的,而在 GPU 渲染 ing 的时候,CPU 为 GPU 准备了渲染的资源,也就是开辟了一块待渲染的缓冲区,这样等当 GPU 渲染接下来的内容的时候直接从缓冲区内取就 OK 了,这便是离屏渲染。

当然与之对应的就是当前屏幕渲染,我们直接称之为屏幕渲染就好了。在进行 iOS 开发过程中,我们大部分操作的都是 UIKit 中的组件,这些组件正常的渲染都是屏幕渲染,只有在一些特殊的情况下才会使用离屏渲染。比如,在做动画的时候,通过使用屏幕渲染可能速度上会慢,所有就可以通过离屏渲染来加速,而且一旦你的缓存区可以重复利用,那么效率的提高是不是很爽(就如同 UITableViewCell 的重用机制)

离屏渲染检测

离屏渲染的检测目前可以通过 Xcode 来进行检测。Xcode 9.x 之后,这部分功能在 Xcode 中集成,之前的是在 Instruments 中的 Core Animation 工具中检测。
如图,真机调试,在 Xcode 的 Debug -> View Debugging -> Rendering -> Color OffScreen-Render Yellow 选中

然后就可以在手机的 app 中看到开启了离屏渲染的视图,这样我们就可以在实际开发中找到哪些视图开启了离屏渲染

如果是 Simulator 的话,直接在 Simulator 的 Debug 菜单栏中选中 Color OffScreen-Render Yellow 也可以进行检测。

哪些操作会引发离屏渲染

MaskView 的设置

这是为 iOS 中的一些视图添加蒙版的属性, maskView 的属性是在 UIView 中的,iOS 8.0 + 开放的 API

1
2
3
4
UIView  *maskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
maskView.alpha = 0.5;
maskView.backgroundColor = [UIColor blueColor];
self.icon.maskView = maskView;

shouldRasterize = YES;

这个属性开启之后,也会引起离屏渲染

1
self.icon.layer.shouldRasterize = YES;

shouldRasterize 这个属性是称之为 光栅化,如果开启次属性而且缓存区被重复利用,那么效率会有很大提升,这个属性也是可以通过 Xcode 的 Debug -> View Debugging -> Rendering -> Hit Green and Missed Red

其他的可能会引起离屏渲染的,后面遇到了再进行添加!

设置阴影

设置阴影会引起离屏渲染

1
2
3
self.icon.layer.shadowColor = [UIColor blueColor].CGColor;
self.icon.layer.shadowOffset = CGSizeMake(5, 5);
self.icon.layer.shadowOpacity = 1;

设置圆角

这里需要注意了,如果是 UIImageView 设置圆角的话不会发生离屏渲染,但是如果是 UIButton 的话却会,而且 UIImageView iOS 9 之前应该会发生离屏渲染。所以,当我们知道一些调试技术的时候,就能知道苹果到底做了哪些事情了。

1
2
self.mybtn.layer.cornerRadius = 40;
self.mybtn.layer.masksToBounds = YES ;

参考:

  1. iOS 离屏渲染的研究
  2. 离屏渲染学习笔记- 王中周的技术博客
  3. iOS离屏绘制的性能和机制分析
  4. objc 绘制像素到屏幕上
  5. iOS 图形处理和性能

GitHub (一) 提交了代码,格子没有变绿色

发表于 2018-07-25 | 分类于 GitHub

这篇就解决一个问题: 为什么在 GitHub 上提交了代码,然而上面的格子却没有变成绿色 ?

我提交 Git 的时间应该还是算比较频繁的,但是发现一个问题,就是今天提交了,但是格子却没有变成绿色,先前一直没有注意这个问题

其实我基本上每天都是有一些提交的,但是就是不显示绿色 …
但我还是发现了规律,如果我创建过 Git 仓库,那么那天就会变成绿色,我傻傻的以为,这个格子只有在创建了仓库之后才会变成绿色,😶

原因

原因就一句话:GitHub 上绑定的邮箱和 Git 提交的邮箱不一致,我之前本地因为工作的原因全局设置了 @163 的邮箱,但是 GitHub 上绑定的是 @gmail 邮箱。

解决

  1. 修改 GitHub 上的邮箱,这个我没有实际修改过,一致就 OK 吧
  2. 修改本地邮箱,git config –global user.email ‘xxxx@gmail.com’(查看本地的直接用 git config user.email)

修改完成后,提交一下,就可以看到绿色的格子了 … 有些事你还真得多想想!

Git 命令指南

1
2
3
4
5
6
7
8
9
10
11

# maple @ iMac in ~/Maple [14:30:37]
$ git config user.email
18576759510@163.com

# maple @ iMac in ~/Maple [14:30:45]
$ git config --global user.email 'huidragonaijy@gmail.com'

# maple @ iMac in ~/Maple [14:31:08]
$ git config user.email
huidragonaijy@gmail.com

上面的命令:


  1. 查看本地 Git Email 地址;
  2. 修改本地 Git Email 地址;
  3. 再次查看是否修改成功

RunLoop(一)

发表于 2018-07-23 | 分类于 RunLoop

故事

RunLoop ,运行时循环。

iOS App 启动之后,当前进程开启主线程之后,为了保证主线程一直在运行就在主线程中开启了 RunLoop。RunLoop 在运行过程中,如果有事件(比如 timer、Source、Observe)就会进行处理,如果没有事件就会在一种休眠状态。

在真正了解 RunLoop 之前,估计我就只能说出上面这一串解释说明了,如果再继续说的话,就应该是响应者链了。但是,经过最近一段时间学习发现 RunLoop 真是无处不在,只不过我们没有注意吧了。

比如:

  1. Timer 的运行与 RunLoop 有关,主要是 mode 方面;
  2. GCD 的 dispatch_get_mainQueue 也与 RunLoop 有关系,毕竟都是在主线程中的事情;
  3. ScrollView/TextView 的滑动业余 RunLoop 有关,主要是模式之间的切换;
  4. App 的卡顿与 RunLoop 也有关,这个我倒是还不知道什么事情

…

真心发现 RunLoop 是一个很大的模块,之前一直忽略了,这次抽时间好好的细致的了解一下 RunLoop 中的知识。

RunLoop API

RunLoop 的 API 有两套,一套是 OC 的,名字是 NSRunLoop; 另一套是 C 语言的 CFRunLoop。 后者是开源的

针对于 NSRunLoop,可以看一下文档介绍,大体意思为:
NSRunLoop 是编程接口,这套编程接口主要用来处理输源。
一个 NSRunLoop 对象负责处理键盘、鼠标、NSPort、NSConnection 以及 NSTimer 事件。你的应用开发中不能直接创建和管理 NSRunLoop。每一个 NSThread 对象,包括 mainThread 都包含了一个 NSRunLoop 对象,这个对象根据需要自动创建。如果你想要查看当前的 RunLoop 对象,可以通过 currentRunLoop 获取。

需要注意的是:
NSRunLoop 不是线程安全的,所以不要在多线程中使用 NSRunLoop。

我读过的博客

发表于 2018-07-23

iOS

  1. Glow 技术团队博客, 这是 Glow 团队的技术博客,其中印象比较深刻的有:runtime 的黑魔法,以及 ScrollView 的自动布局等其他用法,UITableView 图片加载的优化,还有一个是 KVO 的原理以及自己实现,这里的博客不仅仅有 iOS 的还有 Android 和 后台以及统计等方面的,非常适合阅读,我是花了一早上时间翻阅了所有的博客的,记忆深刻的是 Https 的过程
  2. objc.io 英文 & objc.io 中文,这里全部都是 iOS 的了,推荐直接看中文的吧,感觉每一篇都比较深刻,不是读一遍两遍可以体会到东西的,反正目前我还没有读完 … … 不知道是读不懂,还是怎么样,看着不错,但是读完了却没有学习到什么,可能是太深刻了
  3. NSHipster 中文,这里的博客也是深度好文,不过觉得也是浅尝辄止吧,如果要学习东西的话,还是需要搜索其他的相关信息
  4. 落影 博客里面有关于算法和音视频方面的知识,以及一些面试指导 还有 OpenGL
  5. Joy__博客,质量比较高,是通过趣直播发现的,然后看了下博客以及 GitHub,目前应该是就只与今日头条。博客中的内容主要是针对于 app 性能检测的,偏底层多一些

Java

Node JS

Android

C

混合

Node JS (一)

发表于 2018-07-22

Hexo(四) 添加动态背景效果canvas-nest

发表于 2018-07-22 | 分类于 Hexo

效果就如同你看到的背景一样,这种背景在一些博客中经常会看到,那么如何来进行设置呢?

非常简单,在 next theme 主题下,找到 _config.yml 文件,然后在文件末尾添加:

1
2
3
4
5
6
# --------------------------------------------------------------
# background settings
# --------------------------------------------------------------
# add canvas-nest effect
# see detail from https://github.com/hustcc/canvas-nest.js
canvas_nest: true

执行:hexo clean; hexo g; hexo s 就可以看到效果了!

1…345…13
Alice

Alice

The Loneliest Whale in the World

121 日志
35 分类
30 标签
© 2021 Alice
由 Hexo 强力驱动
主题 - NexT.Pisces