iOS面试题整理---空指针和野指针

一、什么是空指针和野指针

1.空指针

1> 没有存储任何内存地址的指针就称为空指针(NULL指针)

2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。

下面两个都是空指针:

Student *s1 = NULL;

Student *s2 = nil;

2.野指针

"野指针"不是NULL指针,是指向"垃圾"内存(不可用内存)的指针。野指针是非常危险的。

 

二、野指针和空指针例子

接下来用一个简单的例子对比一下野指针和空指针的区别

1.首先,打开Xcode的内存管理调试开关,它能帮助检测垃圾内存

 

 2.自定义Student类,在main函数中添加下列代码

Student *stu = [[Student alloc] init];
[stu setAge:10];
[stu release];
[stu setAge:10];

运行程序,你会发现第7行报错了,是个野指针错误!

 3.接下来分析一下报错原因

1> 执行完第1行代码后,内存中有个指针变量stu,指向了Student对象

Student *stu = [[Student alloc] init];

假设Student对象的地址为0xff43,指针变量stu的地址为0xee45,stu中存储的是Student对象的地址0xff43。即指针变量stu指向了这个Student对象。

2> 接下来是第3行代码

[stu setAge:10];

这行代码的意思是:给stu所指向的Student对象发送一条setAge:消息,即调用这个Student对象的setAge:方法。目前来说,这个Student对象仍存在于内存中,所以这句代码没有任何问题。

3> 接下来是第5行代码

[stu release];

这行代码的意思是:给stu指向的Student对象发送一条release消息。在这里,Student对象接收到release消息后,会马上被销毁,所占用的内存会被回收。

(release的具体用法会放到OC内存管理中详细讨论)

Student对象被销毁了,地址为0xff43的内存就变成了"垃圾内存",然而,指针变量stu仍然指向这一块内存,这时候,stu就称为了野指针!

4> 最后执行了第7行代码

[stu setAge:10];

这句代码的意思仍然是: 给stu所指向的Student对象发送一条setAge:消息。但是在执行完第5行代码后,Student对象已经被销毁了,它所占用的内存已经是垃圾内存,如果你还去访问这一块内存,那就会报野指针错误。这块内存已经不可用了,也不属于你了,你还去访问它,肯定是不合法的。所以,这行代码报错了!

4.如果改动一下代码,就不会报错

Student *stu = [[Student alloc] init];
[stu setAge:10];
[stu release];
stu = nil;
[stu setAge:10];

注意第7行代码,stu变成了空指针,stu就不再指向任何内存了

因为stu是个空指针,没有指向任何对象,因此第9行的setAge:消息是发不出去的,不会造成任何影响。当然,肯定也不会报错。

 

5.总结

1> 利用野指针发消息是很危险的,会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。

2> 利用空指针发消息是没有任何问题的,也就是说下面的代码是没有错误的:

[nil setAge:10];

 上述转载于:https://www.cnblogs.com/mjios/archive/2013/04/22/3034788.html




上面讲的是MRC情况,那么ARC情况下,哪些情形可能导致野指针呢?

 

 

>>>block 多线程 Hook 甚至代理都有可能出现吧???

 

>>>用assign修饰对象就出现导致野指针

转载:https://www.jianshu.com/p/14e947dab4f9

开始实验

首先定义一个简单的object类型,重写dealloc方便查看对象的释放
@interface TestObject : NSObject
@property (nonatomic, assign) NSInteger age;
@end

@implementation TestObject
- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}
@end

@interface BasicViewController : BaseViewController
@property (nonatomic ,assign) TestObject *property1;
@end
测试一
- (void)test {
    self.property1 = [TestObject new];
    self.property1.age = 1;
}

测试二
- (void)test {
    TestObject *obj = [TestObject new];
    self.property1 = obj;
    self.property1.age = 1;
}
实验结果和分析
测试一结果:测试一 会在self.property1 = [TestObject new];这一行报出警告,单步调试后发现TestObject对象会在创建完以后被释放,所以下面self.property1.age = 1属性赋值将直接引发crash:EXC_BAD_ACCESS,这就是是所谓的野指针。
分析:因为TestObject new对象创建出来没有任何强指针指向它,所以创建完以后立即会被释放了,这时候self.property1指针指向的地方已经不可用,所以就成了野指针。
测试二结果:编译正常没有警告,运行正常也没有报错,但是在test方法调用之后如果你再去调用属性property1同样会引发EXC_BAD_ACCESS。
分析:因为objtest方法内的临时变量,ARC模式下编译器会在test方法的末尾调用obj release,所以一旦test方法执行完毕以后,该临时变量就会被释放,此时self.property1将变为野指针。由于这种情况下在编译阶段我们的代码都不会有任何的警告,而运行时该属性变为野指针之后,我们在任何地方调用就会引发野指针崩溃,这是很危险的。在快速编码的时候,很有可能由于我们的复制粘贴属性的定义而把object类型属性用assign修饰,因为曾经就遇到过同事留下的这种坑,从逻辑上找根本找不错错误,仔细检视代码才发现了这个低级错误

 

 


 

转载  https://www.jianshu.com/p/8aba0ee41cd7

在iOS9之前,系统库的delegatetarget-action有一部分是assign(unsafe_unretain)的形式,这时候如果内存在别处被回收了,也是会出现野指针的。
所以iOS9之后这些地方就改成了weak内存修饰符,内存被回收的时候通过weak表,把这些指针设为nil。也大幅度减少了野指针的出现。如果现在在工程中依然频繁出现野指针,几乎可以肯定是错误地使用了内存。

表现:Crash

对于MachUnixNSException三种不同层级的crash,NSException比较好说,可以直接定位到OC代码。问题主要来自EXC_BAD_ACCESS(SIGSEGV)这种异常,难以在我们的应用代码中定位。
image
  • SIGILL 执行了非法指令,一般是可执行文件出现了错误
  • SIGTRAP 断点指令或者其他trap指令产生
  • SIGABRT 调用abort产生
  • SIGBUS 非法地址。比如错误的内存类型访问、内存地址对齐等
  • SIGSEGV 非法地址。访问未分配内存、写入没有写权限的内存等
  • SIGFPE 致命的算术运算。比如数值溢出、NaN数值等
实际我们遇到Mach Exception绝大部分都是野指针的问题。SIGSEGV/SIGABRT/SIGTRAP 比较多见。
野指针问题表现千奇百怪,而且因为崩溃的地方并不是造成野指针的地方,而且难以重现,所以问题往往难以定位。
image

腾讯Bugly的这张图可以看到,野指针几乎可以造成各种类型的Mach Exception

 

T

转载于:https://www.cnblogs.com/1-434/p/10494216.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值