一、访问了一个已经被释放的对象
在不使用 ARC 的时候,内存要自己管理,这时重复或过早释放都有可能导致 Crash。
例子
1 2 3 4 5 |
NSObject * aObj = [[NSObject alloc] init]; [aObj release]; NSLog(@"%@", aObj); |
原因
aObj 这个对象已经被释放,但是指针没有置空,这时访问这个指针指向的内存就会 Crash。
解决办法
- 使用前要判断非空,释放后要置空。正确的释放应该是:
123[aObj release];aObj = nil;
由于ObjC的特性,调用 nil 指针的任何方法相当于无作用,所以即使有人在使用这个指针时没有判断至少还不会挂掉。在ObjC里面,一切基于 NSObject 的对象都使用指针来进行调用,所以在无法保证该指针一定有值的情况下,要先判断指针非空再进行调用。
1234if (aObj) {//...}常见的如判断一个字符串是否为空:
12if (aString && aString.length > 0) {//...} - 适当使用 autorelease。有些时候不能知道自己创建的对象什么时候要进行释放,可以使用 autoRelease,但是不鼓励使用。因为 autoRelease 的对象要等到最近的一个 autoReleasePool 销毁的时候才会销毁,如果自己知道什么时候会用完这个对象,当然立即释放效率要更高。如果一定要用 autoRelease 来创建大量对象或者大数据对象,最好自己显式地创建一个 autoReleasePool,在使用后手动销毁。以前要自己手动初始化 autoReleasePool,现在可以用以下写法:
123456@autoreleasepool{for (int i = 0; i < 100; ++i) {NSObject * aObj = [[[NSObject alloc] init] autorelease];//....}}
二、访问数组类对象越界或插入了空对象
NSMutableArray/NSMutableDictionary/NSMutableSet 等类下标越界,或者 insert 了一个 nil 对象。
原因
一个固定数组有一块连续内存,数组指针指向内存首地址,靠下标来计算元素地址,如果下标越界则指针偏移出这块内存,会访问到野数据,ObjC 为了安全就直接让程序 Crash 了。
而 nil 对象在数组类的 init 方法里面是表示数组的结束,所以使用 addObject 方法来插入对象就会使程序挂掉。如果实在要在数组里面加入一个空对象,那就使用 NSNull。
1 2 |
[array addObject:[NSNull null]]; |
解决办法
使用数组时注意判断下标是否越界,插入对象前先判断该对象是否为空。
1 2 3 4 |
if (aObj) { [array addObject:aObj]; } |
可以使用 Cocoa 的 Category 特性直接扩展 NSMutable 类的 Add/Insert 方法。比如:
1 2 3 4 5 6 7 8 9 10 11 12 |
@interface NSMutableArray (SafeInsert) -(void) safeAddObject:(id)anObject; @end @implementation NSMutableArray (SafeInsert) -(void) safeAddObject:(id)anObject { if (anObject) { [self addObject:anObject]; } ss="crayon-sy">[self addObject:anObject]; } t-monaco crayon-os-pc print-yes notranslate" data-settings=" minimize scroll-always" style=" margin-top: 12px; margin-bottom: 12px; font-size: 13px !important; line-height: 15px !important;">
原因aObj 这个对象已经被释放,但是指针没有置空,这时访问这个指针指向的内存就会 Crash。 解决办法
二、访问数组类对象越界或插入了空对象NSMutableArray/NSMutableDictionary/NSMutableSet 等类下标越界,或者 insert 了一个 nil 对象。 原因一个固定数组有一块连续内存,数组指针指向内存首地址,靠下标来计算元素地址,如果下标越界则指针偏移出这块内存,会访问到野数据,ObjC 为了安全就直接让程序 Crash 了。 而 nil 对象在数组类的 init 方法里面是表示数组的结束,所以使用 addObject 方法来插入对象就会使程序挂掉。如果实在要在数组里面加入一个空对象,那就使用 NSNull。
解决办法使用数组时注意判断下标是否越界,插入对象前先判断该对象是否为空。
可以使用 Cocoa 的 Category 特性直接扩展 NSMutable 类的 Add/Insert 方法。比如:
这样,以后在工程里面使用 NSMutableArray 就可以直接使用 safeAddObject 方法来规避 Crash。 三、访问了不存在的方法ObjC 的方法调用跟 C++ 很不一样。 C++ 在编译的时候就已经绑定了类和方法,一个类不可能调用一个不存在的方法,否则就报编译错误。而 ObjC 则是在 runtime 的时候才去查找应该调用哪一个方法。 |