从业iOS以来,基本的一些知识点总结,尽量用自己的话通俗易懂的方式去解释,原创,不定时更新。有不完善或者错误的地方,欢迎指正,感谢
一些基础概念
-
category(类别):
应用场景:某些地方,只需要给个别类使用的某些方法,避免子类使用。
特点:没有属性列表,可以扩展方法,但是可以通过关联对象添加属性 -
extension(扩展):
场景:平时的.m文件都是,添加方法和实例变量
两者区别:
①类别中原则上只能增加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已);
②类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的(
用范围只能在自身类,而不是子类或其他地方);
③类扩展中声明的方法没被实现,编译器会报警,但是类别中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中。
④类扩展不能像类别那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。
⑤定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。
- 内存分区:代码区、数据区、堆区、栈区
内存管理
- 1.iOS的内存管理分MRC 、ARC、自动释放池。
- 2.iOS的每个一对象都有一个与之对应的引用计数器,叫retaincount,当调用alloc,new, copy方法时,retaincount会加1,当对象调用release方法时,retaincount会减1,当他减到0时,对象就释放了,调用dealloc方法
- 3.自动释放池并不会销毁对象,而是给作用域的对象发送了release消息。
- 3.我觉得这主要是解决了数据共享的问题。
内存管理详解
内存布局:
----------------
内核区:用于操作系统的使用
---------------- 0xc0000000
栈区:函数、方法、临时变量、指针 等等
V
V
V
----------------
两边往中间靠,如果相遇了,造成了堆栈溢出
----------------
^
^
^
^
堆区:存放alloc, new变量
----------------
未初始化的数据 .bss (静态区)
----------------
已初始化的数据 .data (常量区)
----------------
代码区:代码段 .text
---------------- 0x00400000
保留区
----------------
栈区 0x7 存放常量,静态变量。
堆区 0x6 存放对象
对象取值过程: 通过指针(栈区),获取到堆区的内存空间,读到值
原则一: 多用函数,嵌套方法,用空间换时间
TaggedPointer : 简单的小对象 存储, 它的地址不再地址,而是真正的值,不存于堆区。
isa: 被优化过的联合体(union),每一位的值代表不同的含义
离散表 sideTable:包含自旋锁,引用计数
对象的 retainCount = 1 + extra_rc + sideTable。在调用retain 的时候,首先会在isa 的 extra_rc 里面 +1,如果超出了最大值,这会将其中的一半 RC_HALF 保存到散列表里。在调用release 方法时,先在 extra_rc 里 -1,如果为0了,查找散列表中是否有值,有的话 取出 RC_HALF-1 存到extra_rc中,如果此时 retainCount 为 0 了,则发送 dealloc消息
自动释放池原理
1. 首先是什么:自动释放池是一个结构体,包含了 AutoreleasePoolPush,AutoreleasePoolPop。是以 AutoreleasePoolPage 作为结点的双向链表来实现的。
2. 其次作用是什么:自动释放池的作用,在它的作用空间中,大括号内,将对象添加到自动释放池中,随后结束的时候,给对象发送release消息,延迟释放对象。
3. 怎么添加的:page 对象包含了 parent,child,等信息,在 压栈push 对象之前,会先插入一条边界符,并将当前的 page 对象setHot 激活状态,等容器存满之后,再插入边界符,开启下一个 page 容器,child 指向 最新的那个page,新的 page 就会设置setHot
4. 怎么pop:找到最底下的 page,将其中的对象一个一个发送release 消息,然后遇到 边界符 时,会先找到 parent,然后将其 child 置为 nil,并setHot。一直递归,直到所有的都释放了。
5. runloop与自动释放池的关系:runloop 在响应事件时,会调用 autoreleasePoolPush,将后续的一系列对象添加到自动释放池中,最后它休眠又会去调用 pop。从而达到销毁对象的目的。
KVC && KVO
- KVC 键值编码,通过字符串的形式来间接的访问对象属性,给对象属性赋值,而非通过setter,getter方法。这样就可以动态的访问和修改对象属性了。
- KVC原理:待更新
对象通信( KVO & 通知 & 代理 & Block)
runtime
runloop
-
- runloop的作用
- 保持程序的持续运行
- 处理App的各种事件(触摸,定时器,performSelector)
- 节省CPU资源,提高程序的性能
- 2.runloop与线程的关系:runloop是在线程中创建的,子线程runloop默认是不开启的
- 3 timer依赖于runloop
- 4 runtime深入分析。
runtime包含了mode,observe、source、timer,执行流程如下:
1 通知 observers 即将进入 run loop
2 通知 observes 即将开始处理 timer source
3 通知 observes 即将开始处理 source0 事件
4 执行 source0 事件
5 如果处于主线程有 dispatchPort 消息,跳转到第9步
6 通知 observes 线程即将进入休眠
7 内循环阻塞等待接收系统消息,包括:
8 收到内核发送过来的消息 (source1消息)
9 定时器事件需要执行
10 run loop 的超时时间到了
11 手动唤醒 run loop
12 通知 observes 线程被唤醒
13 处理通过端口收到的消息:
14 如果自定义的 timer 被 fire,那么执行该 timer 事件并重新开始循环,完成后跳转到第2步
15 如果 input source 被 fire,则处理该事件
16 如果 run loop 被手动唤醒,并且没有超时,完成后跳转到第2步
17 通知 observes run loop 已经退出
网友评论