空指针,野指针,悬挂指针

当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针)

某些编程语言允许未初始化的指针的存在,而这类指针即为野指针。

悬垂指针的成因:

在许多编程语言中(比如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 &num;
}

调用 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指针,其它地方的某个指针不知道这里释放了,依然指向了这里。产生了野指针。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值