[置顶] iOS 常见7种crash及解决方法

一、访问了一个已经被释放的对象

在不使用 ARC 的时候,内存要自己管理,这时重复或过早释放都有可能导致 Crash。

原因

aObj 这个对象已经被释放,但是指针没有置空,这时访问这个指针指向的内存就会 Crash。

解决办法

  • 使用前要判断非空,释放后要置空。
  • 适当使用 autorelease。

二、访问数组类对象越界或插入了空对象

NSMutableArray/NSMutableDictionary/NSMutableSet 等类下标越界,或者 insert 了一个 nil 对象。

原因

一个固定数组有一块连续内存,数组指针指向内存首地址,靠下标来计算元素地址,如果下标越界则指针偏移出这块内存,会访问到野数据,ObjC 为了安全就直接让程序 Crash 了。

而 nil 对象在数组类的 init 方法里面是表示数组的结束,所以使用 addObject 方法来插入对象就会使程序挂掉。如果实在要在数组里面加入一个空对象,那就使用 NSNull。

解决办法

使用数组时注意判断下标是否越界,插入对象前先判断该对象是否为空。

可以使用 Cocoa 的 Category 特性直接扩展 NSMutable 类的 Add/Insert 方法。

ObjC 则是在 runtime 的时候才去查找应该调用哪一个方法。而对 ObjC 来说,事实上他的实现是一种消息传递而不是方法调用。

调用一个不存在的方法,可以编译通过,运行时直接挂掉,报 NSInvalidArgumentException 异常.

解决方案

像这种类型的错误通常出现在使用 delegate 的时候,因为 delegate 通常是一个 id 泛型,所以 IDE 也不会报警告,所以这种时候要用 respondsToSelector 方法先判断一下,然后再进行调用。

四、字节对齐

可能由于强制类型转换或者强制写内存等操作,CPU 执行 STMIA 指令时发现写入的内存地址不是自然边界,就会硬件报错挂掉。iPhone 5s 的 CPU 从32位变成64位,有可能会出现一些字节对齐的问题导致 Crash 率升高的。

解决办法

使用 memcpy 来作内存拷贝,而不是直接对指针赋值。

五、堆栈溢出

一般情况下应用程序是不需要考虑堆和栈的大小的,总是当作足够大来使用就能满足一般业务开发。但是事实上堆和栈都不是无上限的,过多的递归会导致栈溢出,过多的 alloc 变量会导致堆溢出。

六、多线程并发操作

这个应该是全平台都会遇到的问题了。当某个对象会被多个线程修改的时候,有可能一个线程访问这个对象的时候另一个线程已经把它删掉了,导致 Crash。比较常见的是在网络任务队列里面,主线程往队列里面加入任务,网络线程同时进行删除操作导致挂掉。

解决方法

  • 加锁

  • NSLock
    普通的锁,加锁的时候 lock,解锁调用 unlock。

  • 可以使用标记符 @synchronized 简化代码

  • NSRecursiveLock 递归锁

  • NSConditionLock 条件锁

  • NSDistributedLock 分布式锁

    1. 无锁
      放弃加锁,采用原子操作,编写无锁队列解决多线程同步的问题

    七、Repeating NSTimer

如果一个 Timer 是不停 repeat,那么释放之前就应该先 invalidate。非repeat的timer在fired的时候会自动调用invalidate,但是repeat的不会。这时如果释放了timer,而timer其实还会回调,回调的时候找不到对象就会挂掉。

原因

NSTimer 是通过 RunLoop 来实现定时调用的,当你创建一个 Timer 的时候,RunLoop 会持有这个 Timer 的强引用,如果你创建了一个 repeating timer,在下一次回调前就把这个 timer release了,那么 runloop 回调的时候就会找不到对象而 Crash。

更详尽的描述

对于崩溃问题可以尝试使用 Project->Profile, 连上真机,选择Devices, 然后弹出的instruments-》Zombie. 运行程序,直到程序崩溃。
这时候工具就会捕获到崩溃代码同时提供引用基数产生及释放的全部过程。

如果你是通过xCode来查看崩溃的位置,有时候会不正确。这个要多加注意。

你可能感兴趣的:(ios,Crash)