不错的博客:https://www.jianshu.com/p/33ee5e7d312c
最近在调研野指针的定位工具,对野指针有了更深入的理解,写篇文章总结下。
一、那什么是野指针?这是维基百科上的定义:
当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称迷途指针(即通常说的野指针)。
可以看出普通指针变成野指针需要满足两个条件:
1、其所指向的对象被释放或者收回,2、其本身没有做任何的修改。
// ARC环境下:
__unsafe_unretained UIView *testObj1 = [[UIView alloc] init];
// testObj1 指向的内存区域已释放,本身未做任何修改,已成为野指针,向其发消息会闪退。
__weak UIView *testObj2 = [[UIView alloc] init];
// testObj2 指向的内存区域已释放,但本身被置为nil,不会成为野指针。
需要指出一点:访问野指针本身是没有问题的,不会引起异常;只有使用野指针时才会异常(比如OC里给对象发消息),表现就是闪退。
// ARC环境下
__unsafe_unretained UIView *testObj = [[UIView alloc] init];
NSLog(@"testObj 指针指向的地址:%p 指针本身的地址:%p", testObj, &testObj);
[testObj setNeedsLayout];
// 可以看到NSlog打印不会闪退,调用[testObj setNeedsLayout];会闪退
二、什么情况下会产生野指针?
从产生过程上分析,有以下几种情况:
(1)指针变量未初始化:任何指针变量刚被创建时不会自动成为NULL指针,所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
(2)指针释放之后未置空:有时指针在dealloc或free后未赋值 NULL\nil,dealloc或free后它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。
释放后的指针应立即将指针置为NULL\nil,防止产生“野指针”。
(内存泄漏可能会导致野指针。
内存泄漏指程序在动态分配内存后没有及时释放,导致系统无法再次使用这些内存。如果指针变量指向的内存空间已经被释放,而指针变量没有被置为NULL或者被重新指向有效的内存地址,就会出现野指针。
举个例子,如果程序中使用malloc函数动态分配内存,但是在使用完后没有使用free函数释放内存,那么这块内存就会泄漏。如果指向这块已经泄漏的内存的指针没有被置为NULL或者被重新指向有效的内存地址,就可能出现野指针。因此,为了避免野指针问题,程序中应该注意及时释放已经动态分配的内存,避免内存泄漏问题)
(3)指针操作超越变量作用域:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
第一点主要指使用C语言时。但是给初始化的指针赋个默认值是个好习惯,也是有必要的,不同的平台和编译环境对未初始化的指针的处理是有差异的。比如ARC环境下,定义NSInteger num
,num 就可能被初始化成一个极大值。
下图是使用野指针可能出现的情况:
野指针的行为
使用野指针不一定百分百发生crash,这要看当时内存的使用情况,所以野指针的行为是多变不可预测的。野指针指向的地址空间在对象释放时被标记为空闲地址,接下来再去调用该野指针操作将有下列几种行为:
- 该空间一直空闲,调用该属性会引发EXC_BAD_ACCESS;
- 该空间被其他对象申请并占用,当调用该野指针的方法methodA时,这个空间上被分配的新对象并没有方法methodA, 会出现方法找不到:[xxx methodA] unrecognized selector sent to instance 0x1xxxxxxxx;
- 该空间被其他对象申请并占用,当调用该野指针的方法methodA时,这个空间上被分配的新对象刚好也包含方法methodA, 调用之后不会出现任何crash;
作者:不上火喝纯净水
链接:https://www.jianshu.com/p/14e947dab4f9
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三、怎么检测野指针?
Xcode已经提供了比较完善的检测方法,Malloc Scribble
(内存涂鸦)和Zoombie Objects
。也可以自己实现这两种检测方法。
1、实现Xcode的Malloc Scribble功能,Hook系统的free()函数,可以适用于c\c++类,标记每个要释放的对象:修改其指针指向的内存区域,在指针指向的内存区域填充数据(0x55), 使指针指向不可读的内存,再给这个对方发消息时,就会闪退。
!(ios hook框架之——fishhook)[https://www.jianshu.com/p/9e8236758462]
static void (*orig_free)(void *);
void safe_free(void* p){
size_t memSiziee= malloc_size(p);
// 给指针指向的内存区域填充值,指针本身没有发生改变
memset(p, 0x55, memSiziee);
orig_free(p);
return;
}
int main(int argc, char * argv[]) {
@autoreleasepool {
// 使用fishhook库,替换系统的free(void *)函数实现
rebind_symbols((struct rebinding[1]){{"free", safe_free, (void *)&orig_free}}, 1);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
2、实现Xcode的Zoombie Objects功能,实现自己的Zoombie Object(僵尸类),然后Hook系统的- (void)dealloc方法,只适用于oc类,修改对象的 isa 指针,令其指向特殊的僵尸类。僵尸类只有isa指针,没有任何方法实现,如果这个对象再次收到消息,就会闪退。
- (void)dealloc
{
const char *className = object_getClassName(self);
char *zombieClassName = NULL;
do {
Class zombieClass = objc_getClass(zombieClassName);
objc_destructInstance(self); //关键
object_setClass(self, zombieClass);
} while (0);
if (zombieClassName != NULL)
{
free(zombieClassName);
}
}
这两种方法都是在产生野指针后,使用野指针时,让APP崩溃来发现是那里出的问题。也就是说需要尽量覆盖各种场景,才能发现野指针,并不能用扫描的方式提早发现。所以还是需要保证编码质量,从源头上避免出现野指针的问题。
作者:ch32053
链接:https://www.jianshu.com/p/8ad1c09aac72
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
内存涂鸦原理
Xcode的Malloc Scribble功能是一种用于内存调试的工具,它主要通过在已分配和释放的内存区域填充特定的数据模式,来帮助开发者更容易地发现和定位内存使用问题,特别是与野指针相关的问题。
Malloc Scribble的工作原理
分配时填充:当通过malloc(或类似函数)分配内存时,Malloc Scribble会在这些内存区域填充特定的数据(通常是0xAA)。这样做的好处是,如果分配的内存未被正确初始化就被使用,那么访问这些数据时很可能会立即触发错误,从而更容易被开发者发现。
释放后填充:当内存被释放后,Malloc Scribble会在这些内存区域填充另一种特定的数据(通常是0x55)。这样做的目的是,如果之后有野指针尝试访问这些已释放的内存,那么访问到的将是这些特定的数据,而不是随机数据,这有助于识别出野指针问题。
Malloc Scribble的作用
提高野指针的崩溃率:由于Malloc Scribble在释放内存后填充了特定的数据,这使得野指针访问这些内存时更容易触发崩溃,从而提高了野指针问题的可见性。
辅助内存调试:通过填充特定的数据,Malloc Scribble使得开发者在调试过程中更容易识别出内存使用的问题,如未初始化的内存使用、内存越界访问以及野指针等。
如何使用Malloc Scribble
在Xcode中,使用Malloc Scribble功能非常简单。只需在Xcode的scheme配置中,进入Diagnostics选项卡,然后勾选“Malloc Scribble”选项即可。此外,Xcode还提供了其他与内存调试相关的选项,如“Malloc Stack”、“Guard Malloc”和“Address Sanitizer”等,这些工具可以与Malloc Scribble一起使用,以提供更全面的内存调试能力。
注意事项
性能影响:虽然Malloc Scribble是一个非常有用的内存调试工具,但它会对应用程序的性能产生一定的影响。因此,在开发过程中应该谨慎使用,避免在性能敏感的环境下启用。
误报与漏报:虽然Malloc Scribble可以提高野指针问题的可见性,但它也可能产生误报或漏报。因此,在使用过程中需要结合其他调试手段进行综合分析。
总之,Xcode的Malloc Scribble功能是一个强大的内存调试工具,它可以帮助开发者更容易地发现和定位内存使用问题,特别是与野指针相关的问题。然而,在使用过程中也需要注意其性能影响和可能产生的误报或漏报问题。