当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针)
某些编程语言允许未初始化的指针的存在,而这类指针即为野指针。
悬垂指针的成因:
在许多编程语言中(比如C),显示地从内存中删除一个对象或者返回时通过销毁栈帧,并不会改变相关的指针的值。该指针仍旧指向内存中相同的位置,即使引用已经被删除,现在可能已经挪作他用。
一个简单的例子:
{
char *dp = NULL;
/* ... */
{
char c;
dp = &c;
} /* c falls out of scope */
/* dp is now a dangling pointer */
}
/* 打印结果
(lldb) po p
<nil>
(lldb) po p
''
*/
另一个常见原因是混用 malloc() 和 free():当一个指针指向的内存被释放后就会变成悬垂指针。正如上个例子,可以避免这个问题的一种方法是在释放它的引用后把指针重置为NULL。
#include <stdlib.h>
void func()
{
char *dp = malloc(A_CONST);
/* ... */
free(dp); /* dp now becomes a dangling pointer */
dp = NULL; /* dp is no longer dangling */
/* ... */
}
一个很常见的失误是返回一个栈分配的局部变量:一旦调用的函数返回了,分配给这些变量的空间被回收,此时它们拥有的是“垃圾值”。
int *func(void)
{
int num = 1234;
/* ... */
return #
}
调用 func 后,尝试从该指针暂时能读取到正确的值(1234),但是再次调用函数后将会重写栈为 num 分配的的值,再从该指针读取的值就不正确了。如果必须要返回一个指向 num 的指针,num 的作用域必须大于这个函数——它也许被声明为 static。
野指针的成因:
野指针的产生是由于在首次使用之前没有进行必要的初始化。因此,严格地说,在编程语言中的所有为初始化的指针都是野指针。
int f(int i)
{
char *dp; /* dp is a wild pointer */
static char *scp; /* scp is not a wild pointer:
* static variables are initialized to 0
* at start and retain their values from
* the last call afterwards.
* Using this feature may be considered bad
* style if not commented */
}
dp 是一个野指针。scp 不是一个野指针:静态变量一开始被初始化为0,从最后一次调用后保持着它们的值。如果没有注释,使用这个特性也许被视为不良风格。
1.Q:什么是内存泄漏?
A:用动态存储分配函数动态开辟的空间,在使用完毕后没有释放,结果导致一直占据该内存单元,直到程序结束,称为内存泄漏。
野指针就是指向一个已删除的对象或者受限内存区域的指针。 我们写C++的时候强调指针初始化为NULL,强调用完后也为其赋值为NULL,谁分配的谁回收,来避免野指针的问题。 比较常见的就是这个指针指向的内存,在别处被回收了,但是这个指针不知道,依然还指向这块内存
野指针指向的内存没有被覆盖的时候,或者被覆盖成可以访问的内存的时候,不一定会出现崩溃。这个时候向对象发送消息,不一定会崩溃(可能刚好有这个方法),或者向已经释放的对象发送消息。 但是如果野指针指向的是僵尸对象,那就一定会崩溃了,会崩溃在僵尸对象第一次被其它消息访问的时候
系统库的delegate和target-action有一部分是assign(unsafe_unretain)的形式,这时候如果内存在别处被回收了,也是会出现野指针的。
所以iOS9之后这些地方就改成了weak内存修饰符,内存被回收的时候通过weak表,把这些指针设为nil。也大幅度减少了野指针的出现
一般我们在日常的开发中,很少用到__unsafe_unretained,只是在一些开源的源码中看到,那么__unsafe_unretained有什么含义和作用呢?
__unsafe_unretained和__weak一样,表示的是对象的一种弱引用关系,唯一的区别是:__weak修饰的对象被释放后,指向对象的指针会置空,也就是指向nil,不会产生野指针;而__unsafe_unretained修饰的对象被释放后,指针不会置空,而是变成一个野指针,那么此时如果访问这个对象的话,程序就会Crash,抛出BAD_ACCESS的异常。
__unsafe_unretained呢?
1.__weak只支持iOS 5.0和OS X Mountain Lion作为部署版本(当然对于现在,这个原因已经可以无视了)
2.__weak对性能会有一定的消耗,使用__weak,需要检查对象是否被释放,在追踪是否被释放的时候当然需要追踪一些信息,那么此时__unsafe_unretained比__weak快,而且一个对象有大量的__weak引用对象的时候,当对象被废弃,那么此时就要遍历weak表,把表里所有的指针置空,消耗cpu资源。
那么什么时候使用__unsafe_unretained呢?
当你明确对象的生命周期的时候,可以使用__unsafe_unretained替代__weak,可以稍微提高一些性能,虽然这点性能微乎其微。
举个例子,当A拥有B对象,A消亡B也消亡,这样当B存在,A也一定会存在的时候,此时B要调用A的接口,就可以通过__unsafe_unretained 保持对A的引用关系。
比如 MyViewController 拥有 MyView, MyView 需要调用 MyViewController 的接口。MyView 中就可以通过 __unsafe_unretained 保持对MyViewController的引用。
__unsafe_unretained MyViewController * myVC;
我们可以通过代码来看下__weak和__unsafe_retained使用的区别。
通过图1和图2的对比,可以很明显的发现,使用__weak修饰的obj对象在被释放后,指向对象的指针被置空了,而使用__unsafe_unretained修饰的obj对象在被释放后,指向对象的指针成为野指针了,当访问对象addObject方法的时候,抛出异常。
strong:
strong为强引用,强引用也可以理解为持有,强引用一个对象时,也就是会持有一个对象,该对象的引用计数是会加1的。如果不是通过属性来初始化一个对象的话,系统默认帮我们加了_strong修饰来进行内存管理,就是这种情况
NSMutableArray *firstArray = [NSMutableArray array];
那么strong修饰的对象什么时候会被释放呢?
当对象的引用计数为0的时候,该strong对象会被释放
当strong修饰的属性对象,持有该属性对象的对象被释放的时候,通过dealloc方法,会释放掉所有强引用的对象(也就是说被持有的对象)
if ([NSStringFromSelector(invocation.selector) isEqualToString:@"collectionView:didSelectItemAtIndexPath:"]) {
//无痕打点
__unsafe_unretained UICollectionView *collectionView = nil;
id indexPath;
[invocation getArgument:&collectionView atIndex:2];
[invocation getArgument:&indexPath atIndex:3];
[FPPVHelper reportMTAEventId:[collectionView hotTagId] Index:[indexPath row] info:nil];
}
这是某一段神奇的打点代码,不知道谁写的。很明显indexPath此处的修饰符应为__unsafe_unretained,如果为strong的话对象在这里就会被ARC释放掉,然而因为传递的是C指针,其它地方的某个指针不知道这里释放了,依然指向了这里。产生了野指针。