原文地址: https://ctinusdev.github.io/2017/03/03/WriteWildPointer/
野指針的bug應(yīng)該算是最難查的bug之一了祷舀,因為其隨機性強,且難以定位糠馆,下面就終結(jié)了幾類常見的高概率野指針寫法早处。
1席纽、對象釋放后即寡,指針沒有置空伟件。
常見寫法1:
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 396px;">
@property (nonatomic, unsafe_unretained) id obj;
</pre>
|
問題原因:
unsafe_unretained
申明的obj并不會在對象釋放時將指針置空戈毒,如果對象釋放之后艰猬,繼續(xù)使用obj就有可能出現(xiàn)野指針的問題。
解決方案:
盡量使用weak|strong|copy
等來代替unsafe_unretained
來修飾屬性埋市。如果一定要使用unsafe_unretained
冠桃,記得對象釋放后,將指針置空道宅。
常見寫法2:
在objc_setAssociatedObject
方法中該用OBJC_ASSOCIATION_RETAIN_NONATOMIC
修飾的對象誤用成OBJC_ASSOCIATION_ASSIGN
問題原因:
這個問題和上面的常見寫法1問題是類似的食听,就不重復(fù)了。
常見寫法3:
NSNotification/KVO 只addObserver并沒有removeObserver
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 1054px;">
@interface viewController: UIViewController
@property (nonatomic, strong) id obj;
@end
@implementation viewController
-(void)someButtonClick:(id)sender
{
[self.obj addObserver:self forKeyPath:@"someKey" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
-(void)dealloc
{
//沒有移除觀察者
}
@end
</pre>
|
問題原因:
self.obj添加了self作為觀察者后污茵,是通過unsafe_unretained
指針引用的self樱报,如果對象釋放之前不移除觀察,self.obj對應(yīng)keyPath發(fā)生變化時泞当,仍然會去嘗試給self指向的對象發(fā)送通知迹蛤。就可能會出現(xiàn)野指針了。
解決方案:
1襟士、原始方法盗飒,記得addObserver和removeObserver成對出現(xiàn)陋桂。
2逆趣、利用KVOController
2嗜历、對象提前釋放了
常見寫法1:
異步方法block回調(diào)中,沒有強引用self秸脱。
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
5
6
7
8
9
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 302px;">
__weak typeof(self) weakself = self;
[obj method:^(id result) {
[weakself someMethod];
}];
-(void)someMethod
{
self.test = ....;
...
}
</pre>
|
問題原因:
ARC下落包,由于性能原因self既不是strong也不是weak,而是unsafe_unretained
的摊唇。上面代碼block并沒有引用強引用self。若是在執(zhí)行[weakSelf someMethod]時,剛好self被釋放了抹腿,那么self.test 這句的執(zhí)行就有可能造成野指針崩潰。
解決方案:
在進(jìn)入block時旭寿,先強引用weakself警绩。
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
5
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 427px;">
__weak typeof(self) weakself = self;
[obj method:^(id result) {
__strong typeof(self) strongself = weakSelf;
[strongself someMethod];
}];
</pre>
|
常見寫法2:
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
5
6
7
8
9
10
11
12
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 498px;">
@interface viewController: UIViewController
@end
@implementation viewController
-(void)someButtonClick:(id)sender
{
[self.navigationController popViewControllerAnimated:NO];
[self someMethod];
}
@end
</pre>
|
問題原因:
由于self在pop之后就會被釋放,在pop之后肩祥,繼續(xù)使用self,就可能會導(dǎo)致野指針混狠。
解決方案:
在pop和dismiss之后不要在使用self,關(guān)于self的操作都在pop和dismiss之前将饺。
3、對象的多次釋放予弧。
常見寫法1:
多個線程同時對某個對象賦值
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 678px;">
@interface SomeClass: NSObject
@property (nonatomic, strong) NSArray *array;
@end
@implementation SomeClass
- (void)viewDidLoad
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.array = @[@"test"];
});
self.array = @[@"test"];
}
@end
</pre>
|
問題原因:
在調(diào)用setArray:時湖饱,新的值會被retain掖蛤,舊的值會被release井厌。如果兩個線程同時執(zhí)行了setArray:,那么舊的值就可能會release放兩次。
解決方案:
找個問題不難解決旗笔,對象賦之前先加鎖,再賦值就可以解決這類問題蝇恶。
常見寫法2:
CoreFoundation層對象Toll-Free Bridging到Foundation層中,已經(jīng)用了__bridge_transfer關(guān)鍵字轉(zhuǎn)移了對象的所有權(quán)之后撮弧,又對CoreFoundation層對象調(diào)用了一次CFRelease
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">
1
2
3
4
</pre>
|
<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 474px;">
CFUUIDRef uuid = CFUUIDCreate(NULL);
CFStringRef cfString = CFUUIDCreateString(NULL, uuid);
NSString *string = (__bridge_transfer NSString *)cfString;
CFRelease(cfString);
</pre>
|
問題原因:
使用__bridge_transfer
之后,cfString的所有權(quán)已經(jīng)交由ARC處理贿衍,這時再次接手動調(diào)用release,會導(dǎo)致重復(fù)釋放的問題贸辈。
解決方案:
1、將__bridge_transfer
改為__bridge
,不轉(zhuǎn)移對象的所有權(quán)秸仙。
2、去掉CFRelease(cfString)
寂纪;