智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。
普通的指针
C++ 中的普通指针来体现:
1 | Demo *demo = new Demo(); |
这里面会有几个问题:
- 很多时候,会忘记释放指针,容易导致内存泄漏
- 如果
crash()
函数抛出异常了,那么demo指向的对象依然不会被安全删除
智能指针OC中的应用
智能指针
和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命周期
。
oc中的指针只要是retain
属性就默是智能指针,通过引用计数器来实现指针指向的对象的共享。最终控制指针指向的内存的释放。
在oc中基本上所有的对象都是retain
(或者strong
)属性,除了delegate
对象(为了防止循环引用)。
1 | @interface Book : NSObject { |
在oc的对象中,成员都是指针,对于name
,price属性,类对象维护各自那份内存,对于account
属性,有时候会有多个对象共享一份account。
1 | { |
book1与book2的price指针并不共享一块内存,各自维护自己的内存。
1 | { |
上述情况,如果没有使用智能指针,则当book1
和book2
共享同一块内存,如果account
被释放了,那么book2
的currAccount
就变成了野指针,使用时就会crash;
使用智能指针:
虽然currAccount
被release
了,但是currAccount
被book1
对象保留了一次,所以currAccount
并没有被释放,当book1
对象释放currAccount
的时候,他也不一定被释放,因为book2
又保留了它,所以只有当currAccount
的引用计数器为0的时候他才会被释放,在oc中,通过设置retain属性就是智能指针的应用。book1
与book2
对象共享了一块内存currAccount
。通过引用计数器解决共享内存释放的问题。
智能指针原理
scoped_pty
这是比较简单的一种智能指针,正如其名字所述,scoped_ptr
所指向的对象在作用域之外会自动得到析构,scoped_ptr
是non-copyable的,也就是说你不能去尝试复制一个scoped_ptr的内容到另外一个scoped_ptr
中,这也是为了防止错误的多次析构同一个指针所指向的对象。
shared_pty
引用计数器
的指针范围,(和java的GC是一样的逻辑?)
shared_ptr
中所实现的本质是引用计数(reference counting),也就是说shared_ptr
是支持复制的,复制一个shared_ptr
的本质是对这个智能指针的引用次数加1,而当这个智能指针的引用次数降低到0的时候,该对象自动被析构。
如果shared_ptr
所表征的引用关系中出现一个环,那么环上所述对象的引用次数都肯定不可能减为0那么也就不会被删除,为了解决这个问题引入了weak_ptr。
weak_pty
weak_ptr
和shared_ptr
的最大区别在于weak_ptr
在指向一个对象的时候不会增加其引用计数,因此可以用weak_ptr
去指向一个对象并且在weak_ptr
仍然指向这个对象的时候析构它,此时你再访问weak_ptr
的时候,weak_ptr
其实返回的会是一个空的shared_ptr
。
实际上,通常shared_ptr
内部实现的时候维护的就不是一个引用计数,而是两个引用计数,一个表示strong reference,也就是用shared_ptr
进行复制的时候进行的计数,一个是weak reference
,也就是用weak_ptr
进行复制的时候的计数。weak_ptr
本身并不会增加strong reference的值,而strong reference降低到0,对象被自动析构。
为什么要采取weak_ptr
来解决刚才所述的环状引用的问题呢?需要注意的是环状引用的本质矛盾是不能通过任何程序设计语言的方式来打破的,为了解决环状引用,第一步首先得打破环,也就是得告诉C++,这个环上哪一个引用是最弱的,是可以被打破的,因此在一个环上只要把原来的某一个shared_ptr改成weak_ptr,实质上这个环就可以被打破了,原有的环状引用带来的无法析构的问题也就随之得到了解决。
About this Post
This post is written by Aaron Liu, licensed under CC BY-NC 4.0.