Swfit-06.指针(UnsafePointer)

Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的)。
常见的有以下4种类型:
  • UnsafePointer<Pointee>类似于 const Pointee *
  • UnsafeMutablePointer<Pointee> 类似于 Pointee *
  • UnsafeRawPointer 类似于 const void *
  • UnsafeMutableRawPointer 类似于 void *
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    var age = 10
    func test1(_ ptr: UnsafeMutablePointer<Int>) {
    ptr.pointee += 10
    }
    func test2(_ ptr: UnsafePointer<Int>) {
    print(ptr.pointee)
    }
    test1(&age)
    test2(&age) // 20
    print(age) // 20

    var age = 10
    func test3(_ ptr: UnsafeMutableRawPointer) {
    ptr.storeBytes(of: 20, as: Int.self)
    }
    func test4(_ ptr: UnsafeRawPointer) {
    print(ptr.load(as: Int.self))
    }
    test3(&age)
    test4(&age) // 20
    print(age) // 20

Read More

Share

关于指针的强制类型转换

将指针从一种类型强制转换成另一种类型,只改变他的类型,而不改变他的值。

强制类型转换的一个效果是改变指针运算的伸缩。
例如:
如果p是个char 类型的指针,他的值为P,那么表达式
`(int
)p + 7 = P + 28因为int占4字节,而char只占1字节 而:(int )(p + 7) = P + 7`
因为,强制类型转换的优先级高于加法。
**
PS:**
指针+i,表示加i个单元格,即加`i
sizeof(指针本身去掉一个*)`的字节:

1
(int *)p + 7 = P + 7 * sizeof(int) = P + 28

Share

App启动优化

一、冷启动和热启动
  • 定义:
    • 1.关于冷启动:业界对冷启动的定义没有问题,普遍认为是手机开机后第一次启动某个APP。
    • 2.关于热启动:
      对热启动有两种不同的看法:
      • 1.有些人认为是按下home键把APP挂到后台,之后点击APP的icon再拉回来到前台算是热启动;
      • 2.也有些人认为是手机开机后在短时间内第二次启动APP(杀掉进程重启)算是热启动(此时dyld会对部分APP的数据和库进行缓存,所以比第一次启动要快)。

(一般认为APP从后台拉起到前台的时间没啥研究的意义,所以在统计启动时间时,会倾向于后一种说法,不过具体怎么定义看个人,知道其中的区别就好)

如果启动过,内存页就已经加载进内存了,内存页如果要销毁只能是被覆盖,所以这时候的加载进物理内存的时候,实际内存中已经存在值,所以启动就会变快。

Read More

Share

Swfit-05.Array的常见操作

  • map / filter / reduce

    1
    2
    3
    4
    5
    6
    7
    8
    var arr = [1, 2, 3, 4]
    // [2, 4, 6, 8]
    var arr2 = arr.map { $0 * 2 }
    // [2, 4]
    var arr3 = arr.filter { $0 % 2 == 0 } // 10
    var arr4 = arr.reduce(0) { $0 + $1 } // 10
    // 可简写为
    var arr5 = arr.reduce(0, +)
  • map接收函数作为参数

    1
    2
    3
    4
    func double(_ i: Int) -> Int { i * 2 } 
    var arr = [1, 2, 3, 4]
    // [2, 4, 6, 8]
    print(arr.map(double))

Read More

Share

Swfit-04.类,元类,错误处理,泛型,拓展。。。

  • 31. X.self、X.Type、AnyClass
    • X.self(对应OC中的类对象)是一个元类型(metadata)的指针,metadata存放着类型相关信息
    • X.self属于X.Type类型(对应OC中的元类对象)

Read More

Share

Swfit-03.初始化器,可选链,协议。。。

  • 20. 初始化器
    • 类、结构体、枚举都可以定义初始化器
    • 类有2种初始化器:指定初始化器(纵向)(designated initializer)、便捷初始化器(横向)(convenience initializer)
      1
      2
      3
      4
      5
      6
      7
      8
      // 指定初始化器 
      init(parameters) {
      statements
      }
      // 便捷初始化器
      convenience init(parameters) {
      statements
      }

Read More

Share

Swfit-02.闭包,属性,inout,下标,继承。。。

  • 11. 闭包表达式(Closure Expression) :一种函数的定义方式
    • 在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }

      var fn = {
      (v1: Int, v2: Int) -> Int in
      return v1 + v2
      }
      fn(10, 20)

      {
      (v1: Int, v2: Int) -> Int in
      return v1 + v2
      }(10, 20)

      {
      (参数列表) -> 返回值类型 in 函数体代码
      }

Read More

Share

Swfit-01.可变参数,多重可选项,MemoryLayout。。。

  • 1. Playground支持Markup(类似Markdown)语言书写注释。
    //: [上一页](@previous): 上一页
    //: [下一页](@next): 下一页
    #:几个对应几级标题
    -:无序标题
    1.:有序标题
    >:笔记
    ---:分割线
    [图片上传失败...(image-16af49-1606451751395)]:图片
    [blog](http://blogzhou.top):连接
    ** bold **:粗体
    * italic *:斜体

Read More

Share

FMCustomCamera 自定义相机(Swift)

问题: OC中类别(Category)是什么?

Category类别是Objective-C语言中提供的一个灵活的类扩展机制。类别用于在不获悉、不改变原来代码的情况下往一个已经存在的类中添加新的方法,只需要知道这个类的公开接口,而不需要知道类的源代码。类别只能为已存在的类添加新的功能扩展方法,而不能添加新的属性。类别扩展的新方法有更高的优先级,会覆盖同名的原类的已有方法。

Read More

Share

FMCustomCamera 自定义相机(Swift)

Read More

Share

网络基础

内容出自《图解HTTP》

Share

What exactly is RESTful programming?

REST is the underlying architectural principle of the web. The amazing thing about the web is the fact that clients (browsers) and servers can interact in complex ways without the client knowing anything beforehand about the server and the resources it hosts. The key constraint is that the server and client must both agree on the media used, which in the case of the web is HTML.

An API that adheres to the principles of REST does not require the client to know anything about the structure of the API. Rather, the server needs to provide whatever information the client needs to interact with the service. An HTML form is an example of this: The server specifies the location of the resource and the required fields. The browser doesn’t know in advance where to submit the information, and it doesn’t know in advance what information to submit. Both forms of information are entirely supplied by the server. (This principle is called HATEOAS: Hypermedia As The Engine Of Application State.)

Read More

Share

OC/Swift混编的一些小知识

  • Swift调用OC

    桥接头文件:

    1.新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h
    2.在{targetName}-Bridging-Header.h 文件中#import OC需要暴露给Swift的内容
    (可通过编译器自动生成)

@_silgen_name关键字:

1.如果C语言暴露给Swift的函数名跟Swift中的其他函数名冲突了
2.可以在Swift中使用 @_silgen_name 修改C函数名(可以用此方法调用系统底层函数)

1
2
3
4
5
6
7
8
9
10
// C语言
int sum(int a, int b) {
return a + b; }

// Swift
@_silgen_name("sum")
func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32

print(swift_sum(10, 20)) // 30
print(sum(10, 20)) // 30

Read More

Share

Socket原理知识摘要03

socket中TCP的三次握手建立连接详解

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1

  • 客户端再想服务器发一个确认ACK K+1

只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:
socket中发送的TCP三次握手

Read More

Share

Socket原理知识摘要02

我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页 时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是 socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的。本文的主要内容如下:

1.网络中进程之间如何通信?

本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:

  • 消息传递(管道、FIFO、消息队列)

  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)

  • 共享内存(匿名的和具名的)

  • 远程过程调用(Solaris门和Sun RPC)

但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

Read More

Share

Socket原理知识摘要01

  • 什么是TCP/IP、UDP?
    TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
     UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
    这里有一张图,表明了这些协议的关系。
    

    TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。

    Read More

Share

单向散列函数,数字签名,证书

单向散列函数(One-way hash function)

  • 单向散列函数,可以根据根据消息内容计算出散列值

  • 散列值的长度和消息的长度无关,无论消息是1bit、10M、100G,单向散列函数都会计算出固定长度的散列值

    Read More

Share

加密解密

密码的类型

根据密钥的使用方法,可以将密码分为2种

Share

初识Mach-O文件

逆向APP的思路

  • 界面分析
    Cycript、Reveal

  • 代码分析
    对Mach-O文件的静态分析
    MachOView、class-dump、Hopper Disassembler、ida等

  • 动态调试
    对运行中的APP进行代码调试
    debugserver、LLDB

  • 代码编写
    注入代码到APP中
    必要时还可能需要重新签名、打包ipa

    Read More

Share

iOS中的多线程

几种基本概念

  • 进程:
    进程是指 系统中正在运行的一个应用程序,每个进程都运行在其专有的空间内,故一个进程不能访问另外一个进程的独有空间。

  • 线程:
    线程是进程的基本执行单元,进程的所有任务都在线程中执行,故一个进程想要执行任务必须得有一个线程,这个线程也被成为主线程(UI线程)

  • 串行:
    一个任务执行完毕后,再执行下一个任务

  • 并行:
    多个任务同时执行

  • 同步:
    多顾名思义即为一步一步执行线程内的东西

  • 异步:
    不阻塞当前线程操作,等同于后台跑数据
    同步异步是相对于线程(任务)来说的。 串行和并行是相对于队列

    Read More

Share

RunLoop的运行逻辑


Read More

Share

Runtime API

  • Runtime API01 – 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 动态创建一个类(参数:父类,类名,额外的内存空间)
    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

    // 注册一个类(要在类注册之前添加成员变量)
    void objc_registerClassPair(Class cls)

    // 销毁一个类
    void objc_disposeClassPair(Class cls)

    // 获取isa指向的Class
    Class object_getClass(id obj)

    // 设置isa指向的Class
    Class object_setClass(id obj, Class cls)

    // 判断一个OC对象是否为Class
    BOOL object_isClass(id obj)

    // 判断一个Class是否为元类
    BOOL class_isMetaClass(Class cls)

    // 获取父类
    Class class_getSuperclass(Class cls)

Read More

Share

objc_msgSend执行流程

OC中的方法调用,其实都是转换为objc_msgSend函数的调用

objc_msgSend的执行流程可以分为3大阶段

  • 消息发送

  • 动态方法解析

  • 消息转发

    Read More

Share

isa详解

要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针

在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址

从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息

Read More

Share

__block修饰符

block可以用于解决block内部无法修改auto变量值的问题 block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__block int age = 18;
^{
NSLog(@"%d", age);
}();

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age;// by ref
};

struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int size;
int age;
};

Read More

Share

block

  • block本质:

    • block本质上也是一个OC对象,它内部也有个isa指针

    • block是封装了函数调用以及函数调用环境的OC对象

    • block的底层结构如右图所示

Read More

Share

如何实现给分类“添加成员变量”?

默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接实现

  • 关联对象提供了以下API
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 添加关联对象
    void objc_setAssociatedObject(id object, const void * key,
    id value, objc_AssociationPolicy policy)

    // 获得关联对象
    id objc_getAssociatedObject(id object, const void * key)

    // 移除所有的关联对象
    void objc_removeAssociatedObjects(id object)

Read More

Share

+load方法 和 +initialize方法

  • +load

    • +load方法会在runtime加载类、分类时调用

    • 每个类、分类的+load,在程序运行过程中只调用一次

    • 调用顺序
      先调用类的+load
      按照编译先后顺序调用(先编译,先调用)
      调用子类的+load之前会先调用父类的+load
      再调用分类的+load
      按照编译先后顺序调用(先编译,先调用)

      • +load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用

        Read More

Share

.OC对象的本质

NSObject的底层实现:

Read More

Share

常用LLDB指令

print、p:打印

po:打印对象

Read More

Share

几种常见的排序

1.冒泡排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 冒泡排序 */
for (int end = cnt - 1; end > 0 ; end --) {
for (int begin = 1; begin <= end; begin ++) {
// 优化-减少已排序部分的比较
int startIndex = 1;
if (array[begin - 1] < array[begin]) {
int tmp = array[begin - 1];
array[begin - 1] = array[begin];
array[begin] = tmp;
startIndex = begin;
}
end = startIndex;
}
}

Read More

Share

Node环境配置

方式一:安装包的方式安装

  • 安装包下载链接:
  • 安装操作:
    • 一路Next

更新版本

  • 操作方式:
    • 重新下载最新的安装包;
    • 覆盖安装即可;
  • 问题:
    • 以前版本安装的很多全局的工具包需要重新安装
    • 无法回滚到之前的版本
    • 无法在多个版本之间切换(很多时候我们要使用特定版本)

Read More

Share

Favorite Words

那些动听的想法如果不能趁年轻易感的时候去实现,消逝的速度比云还快。


即使有人告诉我,山的那边还是山,我依然想一路小跑翻山越岭,亲自感慨:“我靠!他说的是真的诶!”


与天地等量齐观的眼界


永远不要指望有人会明白你在说什么,别相信什么切肤之痛,别相信什么感同身受,你就是你,独一无二的你,你的那些情绪、想法、念头,别人无法感知,亦无法理解。 —— 独木舟


Read More

Share

My Words

为了锦绣前程也好,
花样人生也罢,
然而,
还是恨透了 别离。


长大,
淡然了许多事,
唯别离愈发的难以释怀。

Share

胡乱写写(My poetry)

远方

我来自哪里
又将去向何方

满地的油菜花
一望无际
花海和天相接的地方
是怎样一副景象

微风轻抚
花儿轻摇


有个声音在轻唤
远方
远方

Read More

Share

如何清理XCode缓存

原文链接

一:移除 Xcode 运行安装 APP 产生的缓存文件(DerivedData)

  • 只要重新运行Xcode就一定会重新生成,而且会随着运行程序的增多,占用空间会越来越大。删除后在重新运行程序可能会稍微慢一点,建议定期清理。

  • 路径:

1
~/Library/Developer/Xcode/DerivedData

Read More

Share

诗歌(inland)

《可遇不可求的事》
— 冯唐
后海有树的院子
夏代有工的玉
此时此刻的云
二十来岁的你

故乡的云,
上古的玉,
随手的诗,
此刻的你。

Read More

Share

诗歌(foreign)

《I Strove with None》
— Walter Savage Landor

I strove with none,
for none was worth my strife;
Nature I lov’d, ,
and next to Nature, Art;
I warm’d both hands before the fire of life;
It sinks,
and I am ready to depart.

我和谁都不争,
和谁争我都不屑;
我爱大自然,
其次就是艺术;
我双手烤着,
生命之火取暖;
火萎了,
我也准备走了。
——[英]兰德(杨绛 译)

Read More

Share

iOS系统音量&屏幕亮度调节

一,系统音量获取

系统框架

1
2
3
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVKit/AVKit.h>

获取系统音量slider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (MPVolumeView *)volumeView {
if (_volumeView == nil) {
_volumeView = [[MPVolumeView alloc] init];
[_volumeView sizeToFit];
#warning 获取系统的音量的UISlider
for (UIView *view in [_volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
self.volumeViewSlider = (UISlider*)view;
break;
}
}
}
return _volumeView;
}

Read More

Share

UIButton重复点击解决方案

项目需求,为了防止用户连续点击,造成重复请求,需要设置UIButton点击后,一段时间间隔内不能点击。这个做开发经常用到,在这小小总结下:
  • 方式一(非主流):
    继承于NSObject写个工具类
    1.声明一个静态变量记录上次的点击时间
    1
    static long LAST_CLICK_TIME1 = 0;

Read More

Share

类似“今日头条”频道编辑功能UI实现

FMChannelEdit

这是一个类似“今日头条”频道编辑功能

前言

  • 站在巨人的肩膀上编程:这个项目的channel编辑页面是在两位前辈代码的基础上,进一步的修改,封装。
    前辈一:codeWorm2015(GitHubID)
    源码地址
    前辈二:HelloYeah(GitHubID)
    源码地址

PS:这两位具体是谁,我也不认识,想和他们具体交流的,请去GitHub上给他们留言,我这能帮你们到这了。

进入正题 (以下均为个人见解,理解不对还望见谅)

Read More

Share

UICollectionView下拉刷新闪一下问题解决

最近写项目遇到UICollectionView的下拉刷新数据回来时,屏幕会闪一下,在网上找了几个方法,亲自试了都不好使,后来自己试了下回主线程刷新UI,发现可以,代码如下:

1
2
3
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});

大家都知道,在子线程刷新UI是很危险的,有时会出现莫名的Bug, 有些情况甚至会直接崩溃。而网络数据的请求一般都是在子线程进行的,数据回来时去刷新UI(UITableView 和 UIConllectionView)但是UITableView在子线程刷新UI没有出现这个Bug(估计苹果做了优化)。

Share

WYYKTScroll控件悬停

效果示例

WYDemo2.gif

这是一个控件悬停的UI效果实现,类似于网易云课堂的详情页UI效果

Read More

Share

如何利用pod trunk发布程序

refer to CoderMJLee

注册
  • pod trunk register 邮箱 '用户名' --description='电脑描述'
查收邮件
接下来查看个人信息

Read More

Share

文件流、网络操作、服务端Web开发基础

文件操作

文件监视

利用文件监视实现自动 markdown 文件转换

Share

node-lesson6

node-lesson6

复习

1.1复习网络编程

  • 什么是网络编程
  • 使用 node 进行网络编程需要使用其它web服务器作为容器吗
  • 什么是协议?什么是http协议?
  • 在网络编程中,为什么要有协议?制定协议有什么好处?

1.2 http协议

Read More

Share

node-lesson5

node-lesson5

1.复习

1.1node的调试

  • 前两样调试用于面试
  • node自带的debug调试命令 n下一步 s步入 o步出
  • node-debug foo.js node-inspector
  • vsc 推荐使用
  • vsc launch.json
  • 抽风式的好使
  • ws

    1.2 es6

  • 用es新特性之前加上严格模式
  • 什么是es?
  • 为什么用es的新标准?
  • 为什么前端不用,node要用?
    es6定义了一个标准—->let命令—->js代码里面写了let—->v8引擎解析,能解析说明支持了es6里的let命令—->node升级了
  • 严格模式
  • let
  • const
  • 块级作用域{}
    let foo =456;
    {
    let foo =123;
    }
    console.log(foo);
  • 字符串的扩展 includes(str) startsWith(str) endsWith(str) repeat(num) ${变量名}
    var bar=’暗示的反思’
    var foo=’123’+’阿斯顿发斯蒂芬’+bar+’是短发飞’;
    var foo2=阿斯顿发斯蒂芬${bar}是短发飞
  • 箭头函数、
  • let const 箭头函数 用来看api使用

    Read More

Share

node-lesson4

#node-lesson4

###1.1什么是io?

1
io 是输入输出 文件操作的读写 网络操作中的请求和应答

###1.2进程和线程?

1
2
3
4
5
进程是为运行中的应用程序提供运行环境的
线程就是执行应用程序当中的代码的
同一时间只干一件事情
node中异步就是帮你完成多线程编程
多线程编程比较复杂

###1.3同步和异步?

1
2
同步会阻塞代码
异步不会阻塞代码

Read More

Share

node-lesson3

#node-lesson3

1.0 nvm常用命令

  • nvm项目地址:nvm-github地址
  • 查看所有已安装的本地的node版本
    nvm list
  • 切换node版本
    nvm use 版本号
  • 安装指定版本的node
    nvm install 版本号 位数
  • 卸载已安装的指定版本的node
    nvm uninstall 版本号

Read More

Share

node-lesson2

node-lesson2

1.1 概念

  • Node.js与JavaScript的关系?
  • Node.js基于哪个JavaScript引擎?
  • Node.js的特点?
  • Node.js只能运行在Windows上吗?

1.2 cmd

  • cd(change directory)切换目录
  • md(make directory)新建目录
  • rd(remove directory)删除非空目录
  • dir(directory)查看目录中的条目
    ls linux
  • ren(rename)重命名文件
  • del(delete)删除文件
  • cls(clear screen)清屏
    clear linux

    Read More

Share

node-lesson1

#node-lesson1

##1.解决几个问题?

###1.前端、后端都是干什么的?
前端 做静态资源的
后端 操控服务器的硬件的

###2.为什么学习node.js?
1.就业有市场
2.有商业用途
国内一些创业公司用用的比较多,功夫熊(做上门保健的,美甲、按摩)
国外的一些大公司都有使用:Facebook、Twitter、Google
国内的一些大公司:Alibaba(天猫所有的页面都是通过Node提供的服务)、Tencent、Baidu
3.本身技术是有价值
4.js开发

###3.什么是JavaScript?
一门脚步语言 需要嵌入html执行

###4.浏览器中的JavaScript可以做什么?
校验表单 操作dom 等等

###5.浏览器中的JavaScript不可以做什么?
操作服务器硬件

Read More

Share

JavaScript 模块化编程

网站越来越复杂,js代码、js文件也越来越多,会遇到什么问题?

1.命名冲突

2.文件依赖问题

什么是模块化

模块化开发演变

全局函数

  • 命名冲突
  • 文件依赖的问题

    Read More

Share

深入理解CADisplayLink和NSTimer

深入理解CADisplayLink和NSTimer

简单地说,它就是一个定时器,每隔几毫秒刷新一次屏幕。

CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。我们在应用中创建一个新的 CADisplayLink 对象,把它添加到一个runloop中,并给它提供一个 targetselector 在屏幕刷新的时候调用。

一但 CADisplayLink 以特定的模式注册到runloop之后,每当屏幕需要刷新的时候,runloop就会调用CADisplayLink绑定的target上的selector,这时target可以读到 CADisplayLink 的每次调用的时间戳,用来准备下一帧显示需要的数据。例如一个视频应用使用时间戳来计算下一帧要显示的视频数据。在UI做动画的过程中,需要通过时间戳来计算UI对象在动画的下一帧要更新的大小等等。

在添加进runloop的时候我们应该选用高一些的优先级,来保证动画的平滑。可以设想一下,我们在动画的过程中,runloop被添加进来了一个高优先级的任务,那么,下一次的调用就会被暂停转而先去执行高优先级的任务,然后在接着执行CADisplayLink的调用,从而造成动画过程的卡顿,使动画不流畅。

duration属性:提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间。该属性在targetselector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:时间=duration×frameInterval。 我们可以使用这个时间来计算出下一帧要显示的UI的数值。但是 duration只是个大概的时间,如果CPU忙于其它计算,就没法保证以相同的频率执行屏幕的绘制操作,这样会跳过几次调用回调方法的机会。

frameInterval属性:是可读可写的NSInteger型值,标识间隔多少帧调用一次selector 方法,默认值是1,即每帧都调用一次。如果每帧都调用一次的话,对于iOS设备来说那刷新频率就是60HZ也就是每秒60次,如果将 frameInterval设为2 那么就会两帧调用一次,也就是变成了每秒刷新30次。

pause属性:控制CADisplayLink的运行。当我们想结束一个CADisplayLink的时候,应该调用-(void)invalidaterunloop中删除并删除之前绑定的targetselector

timestamp属性: 只读的CFTimeInterval值,表示屏幕显示的上一帧的时间戳,这个属性通常被target用来计算下一帧中应该显示的内容。 打印timestamp值,其样式类似于:179699.631584。

Read More

Share