44 (OC)* 内存泄漏、什么情况下会出现内存泄漏、野指针、空指针、nil、NSNull、(僵尸对象、野指针、bad_access )
问题:
1:空指针、野指针、内存泄漏、常见的内存泄漏有哪些?怎样检测和防止内存泄漏
2:nil、Nil、NULL、NSNull的区别 ?
3:bad_access 僵尸对象
一:内存泄漏
程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
1:常见的内存泄漏情况
1:对象之间的循环引用问题
循环引用的实质:多个对象相互之间有强引用,不能施放让系统回收。解决办法:使用 weak
打破对象之间的相互强引用。
2:block的循环引用block
在copy
时都会对block
内部用到的对象进行强引用的。解决办法使用:使用__weak
打破循环的方法只在 ARC
下才有效,在 MRC
下应该使用__block。
__weak typeof(self) weakSelf = self; self.myBlock = ^() { // 除了下面的还有 调用 self的一些属性等等 [weakSelf doSomething] };
3: delegate 的循环引用delegate
是委托模式.委托模式是将一件属于委托者做的事情,交给另外一个被委托者来处理,在这里我们可能会出现委托者和被委托人之间的相互强引用问题;解决办法:在声明 delegate
属性的时候 用weak
进行弱引用 或者 通过中间对象(代理对象)的方式来解决(效率更加高的中间对象NSProxy:
不需要进行发送消息和再动态解析,直接进行消息转发)
@property(nonatomic, weak) id delegate;
4:CADisplayLink
、NSTimer
会对target
产生强引用,
如果target
又对它们产生强引用,那么就会引发循环引用;解决办法:NSTimer
有一个block
的方法,我们可以利用block
的弱指针来解决__weak typeof(self) weakSelf = self;
传 weakSelf
进去
5:通知的循环引用iOS9
以后,一般的通知,都不再需要手动移除观察者,系统会自动在dealloc
的时候调用 [[NSNotificationCenter defaultCenter] removeObserver: self]
。iOS9
以前的需要手动进行移除。原因是:iOS9
以前观察者注册时,通知中心并不会对观察者对象做 retain
操作,而是进行了 unsafe_unretained
引用,所以在观察者被回收的时候,如果不对通知进行手动移除,那么指针指向被回收的内存区域就会成为野指针,这时再发送通知,便会造成程序崩溃。从 iOS9
开始通知中心会对观察者进行 weak
弱引用,这时即使不对通知进行手动移除,指针也会在观察者被回收后自动置空,这时再发送通知,向空指针发送消息是不会有问题的。建议最好加上移除通知的操作:
-(void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self.observer name:@"name" object:nil]; }
6:WKWebView
造成的内存泄漏
总的来说,WKWebView
不管是性能还是功能,都要比 UIWebView
强大很多,本身也不存在内存泄漏问题,但是,如果开发者使用不当,还是会造成内存泄漏。请看下面这段代码:
@property(nonatomic, strong) WKWebView * wkWebView; -(void) webviewMemoryLeak { WKWebViewConfiguration * config = [ [WKWebViewConfiguration alloc] init ]; config.userContentController = [ [WKUserContentController alloc] init ]; [config.userContentController addScriptMessageHandler: self name: @ "WKWebViewHandler"]; _wkWebView = [ [WKWebView alloc] initWithFrame: self.view.bounds configuration: config ]; _wkWebView.backgroundColor = [UIColor whiteColor]; [self.view addSubview: _wkWebView]; NSURLRequest * requset = [NSURLRequest requestWithURL: [NSURL URLWithString: @ "[https://www.baidu.com](https://www.baidu.com/)"]]; [_wkWebView loadRequest: requset]; } // 这样看起来没有问题, 但是其实“ addScriptMessageHandler” 这个操作, 导致了 wkWebView 对 self 进行了强引用, 然后“ addSubview” 这个操作, 也让 self 对 wkWebView 进行了强引用, 这就造成了循环引用。 解决方法就是在合适的机会里对“ MessageHandler” 进行移除操作: -(void) viewDidDisappear: (BOOL) animated { [super viewDidDisappear: animated]; [_wkWebView.configuration.userContentController removeScriptMessageHandlerForName: @ "WKWebViewHandler"]; }
2:内存泄漏的查询
1:Analyze
静态分析 (command + shift + b
)也就是编译,主要分析以下四种问题:
- 逻辑错误:访问空指针或未初始化的变量等;
- 内存管理错误:如内存泄漏等;
- 声明错误:从未使用过的变量;
Api
调用错误:未包含使用的库和框架。
2:Instruments
中的Leak
动态分析内存泄漏,product->profile ->leaks
打开工具主窗口
3:Facebook
早已开源了一款检测内存问题的三方库FBRetainCycleDetector
3: 空指针
没有存储任何内存地址的指针就称为空指针(NULL指针)。
被赋值为nil的指针,在没有被具体初始化之前,为nil。nil、Nil、NULL、NSNULL的含义和区别
nil:OC中的实例对象的空指针
Nil:OC中类的空指针
NULL:C类型的空指针
NSNull:数值类的空对象野指针 ,存储类的NSArray、NSDictionary中的存储的空值。nil或NULL不能做为加到其中
注意:判断对象是否为空(nil、NSNil、@""、@(0) 以上4种)
@interface NSObject (CF) /** * 判断对象是否为空 * 常见的:nil、NSNil、@""、@(0) 以上4种返回YES * 如果需要判断字典与数组,可以自行添加 * @return YES 为空 NO 为实例对象 */ + (BOOL)isEmpty:(id)object; @end @implementation NSObject (CF) + (BOOL)isEmpty:(id)object{ if (object == nil || [object isEqual:[NSNull null]]) { return YES; } else if ([object isKindOfClass:[NSString class]]) { return [object isEqualToString:@""]; } else if ([object isKindOfClass:[NSNumber class]]) { return [object isEqualToNumber:@(0)]; } return NO; } @end
4:野指针
不是nil指针,是指向"垃圾"内存(不可用内存 如:内存被销毁的时候)的指针。
它所指向的内存空间已经被释放,而没有置空,我们也没法访问,给他发送消息会崩溃。
给空指针发送消息不会崩溃。
3:僵尸对象、野指针、EXC_BAD_ACCESS
僵尸对象:内存已经被回收的对象, 但是还是有指针指向该区域。
野指针:指向僵尸对象的指针,向野指针发送消息会导致崩溃。
野指针错误形式在Xcode中通常表现为:EXC_BAD_ACCESS,因为你访问了一块已经不属于你的内存。
1:全局断点调试
用全局断点,看是否可以定位到错误的位置。如图设置一下就OK了。有的时候是可以直接定位到出错的代码位置。
2:Xcode中,可以开启僵尸对象模式。
3:分析-Analyze
使用Xcode来分析你的项目,从Xcode的 Product菜单选择 Analyze或按 Shift-Command-B.Xcode的将需要片刻的时间,但是当它完成的时候你会在左边的 Issue Navigator看到问题列表。由Analyze发现的问题用蓝色高亮显示。
当你点击一个问题,Xcode的会指向问题代码块,这些正是你要的注意的地方。注意,Xcode仅仅是建议。在某些情况下,这是可能的,问题是不相关的,不固定。
4:使用Instruments-Zombies检测