ios內(nèi)存泄漏問題

姓名:賈田田? ? 學(xué)號:17101223395

轉(zhuǎn)載自:http://mp.weixin.qq.com/s/OtWEEo-poTCnCjGxNDak_A? 有改動

【嵌牛導(dǎo)讀】:計算機里有那么多軟件,那么多游戲,每天接收那么特別多的信息,它是怎么儲存的呢,電腦內(nèi)部又是怎么構(gòu)成的?懷著好奇的心,查一查電腦內(nèi)存分布,那么問題來了,內(nèi)存泄漏是怎么回事呢?對計算機的影響有哪些?怎么補救呢?

【嵌牛鼻子】:ios系統(tǒng),內(nèi)存泄漏

【嵌牛問題】:內(nèi)存泄漏怎么補救呢?

【嵌牛正文】:

相信大家都有過重寫 dealloc 方法來檢查某個 view controller 在消失后是否被釋放的經(jīng)歷。這幾乎是 iOS 中尋找由于引用循環(huán)造成內(nèi)存泄漏最有效的方法了。基本上每次發(fā)布,都會做很多次這種事情。不得不說這件事情很無聊,并且很可能會出錯。如果我們在日常的開發(fā)中, 提前的學(xué)習(xí)相關(guān)的知識, 那該多好?

下面是兩個很少見的 UIViewController的屬性:

isBeingDismissed 當(dāng)一個模態(tài)推送出來的 view controller 正在消失的時候, 為: true.

isMovingFromParentViewController ,當(dāng)一個 view controller 正在從它的父 view contrlller 中移除的時候(包括從系統(tǒng)的容器試圖比如說 UINavigationController), 為true.

如果這兩個屬性有一個是 true 的話, 這個 view controller 就會自動的被釋放掉。我們不知道一個 view contrller 完成內(nèi)部狀態(tài)的改變,并且被 ARC 釋放掉需要耗費多長的時間。為了簡單起見,我們假設(shè)它不會超過兩秒。

1.現(xiàn)在看看下面的代碼(文末會有OC版):

extension UIViewController {

? ? public func dch_checkDeallocation(afterDelay delay: TimeInterval = 2.0) {

? ? ? ? let rootParentViewController = dch_rootParentViewController

? ? ? ? // We don’t check `isBeingDismissed` simply on this view controller because it’s common

? ? ? ? // to wrap a view controller in another view controller (e.g. in UINavigationController)

? ? ? ? // and present the wrapping view controller instead.

? ? ? ? if isMovingFromParentViewController || rootParentViewController.isBeingDismissed {

? ? ? ? ? ? let type = type(of: self)

? ? ? ? ? ? let disappearanceSource: String = isMovingFromParentViewController ? "removed from its? ? ? ? ? ? ? ? ? ? parent" : "dismissed"

? ? ? ? ? ? DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: { [weak self] in

? ? ? ? ? ? ? ? assert(self == nil, "(type) not deallocated after being (disappearanceSource)")

? ? ? ? ? ? })

? ? ? ? }

? ? }

? ? private var dch_rootParentViewController: UIViewController {

? ? ? ? var root = self

? ? ? ? while let parent = root.parent {

? ? ? ? ? ? root = parent

? ? ? ? }

? ? ? ? return root

? ? }

}

在延時操作這個閉包中,我們首先通過 [weak self] 來避免這個閉包強引用self。然后通過斷言讓程序在 self 不為空的時候拋出異常。只有存在循環(huán)引用的情況下這個 view controller 才不為空。

現(xiàn)在我們需要做的就是在 viewDidDisappear 中調(diào)用這個方法。只要是你需要檢查它在消失后是不是被釋放掉的 view controller 都需要添加這個方法。

override func viewDidDisappear(_ animated: Bool) {

? ? super.viewDidDisappear(animated)

? ? dch_checkDeallocation()

}

如果發(fā)聲了內(nèi)存泄漏,我們就會得到下面的斷言:

圖片發(fā)自簡書App


這個時候,我們只需要打開 Xcode 的 Memory Graph Debugger 找到并且解決這些循環(huán)引用。

2.另外在 twitter 上也看到了類似的解決方案。

3.使用國人寫的 MLeaksFinder 在每次發(fā)生內(nèi)存泄漏的時候都會彈窗。并且沒有代碼侵入性,只需要使用 CocosPod 導(dǎo)入就可以了。

4.在使用圖片資源的時候,少使用? imageNamed: 方法去獲取使用頻次不高的圖片資源。因為使用 imageNamed:加載的圖片資源會一直存在內(nèi)存里面, 對內(nèi)存的浪費也是巨大的。

5.上面的方法寫了一個 OC 版本的:

.h:

#import <UIKit/UIKit.h>

@interface UIViewController (FindLeaks)

// 默認(rèn)為 NO

@property (nonatomic) BOOL noCheckLeaks;

@end

.m:

//

//? UIViewController+FindLeaks.m

//? Leaks

//

//? Created by sunny on 2017/8/27.

//? Copyright ? 2017年 CepheusSun. All rights reserved.

//

#import "UIViewController+FindLeaks.h"

#import <objc/runtime.h>

static const char *noCheckLeaksKey = "noChechLeaksKey";

@interface NSObject (MethodSwizzling)

+ (void)sy_swizzleInstanceSelector:(SEL)origSelector

? ? ? ? ? ? ? ? ? swizzleSelector:(SEL)swizzleSelector;

@end

@implementation UIViewController (FindLeaks)

#pragma mark - Binding Property

- (BOOL)noCheckLeaks {

? ? return [objc_getAssociatedObject(self, noCheckLeaksKey) boolValue];

}

- (void)setNoCheckLeaks:(BOOL)noCheckLeaks {

? ? objc_setAssociatedObject(self, noCheckLeaksKey, [NSNumber numberWithBool:noCheckLeaks], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

#pragma mark - Check

+ (void)load {

? ? #if DEBUG

? ? [self sy_swizzleInstanceSelector:@selector(viewDidDisappear:) swizzleSelector:@selector(fl_viewDidDisappear:)];

#endif

}

- (void)fl_viewDidDisappear:(BOOL)animated {

? ? [self fl_viewDidDisappear:animated];

? ? if (!self.noCheckLeaks) {

? ? ? ? [self fl_checkDeallocationAfterDelay:2];

? ? }

}

- (void)fl_checkDeallocationAfterDelay:(NSTimeInterval)delay {

? ? UIViewController *root = [self fl_rootParentViewController];

? ? if (self.isMovingFromParentViewController || root.isBeingDismissed) {

? ? ? ? NSString *type = NSStringFromClass([self class]);

? ? ? ? NSString *disappearanceSource = self.isMovingFromParentViewController ? @"removed from its parent" : @"dismissed";

? ? ? ? __weak typeof(self) weakSelf = self;

? ? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

? ? ? ? ? ? NSString *assert = [NSString stringWithFormat:@"%@ not deallocated after being %@",

? ? ? ? ? ? type, disappearanceSource];

? ? ? ? ? ? NSAssert(weakSelf == nil,assert);

? ? ? ? });

? ? }

}

- (UIViewController *)fl_rootParentViewController {

? ? UIViewController *root = self;

? ? while (root.parentViewController) {

? ? ? ? root = root.parentViewController;

? ? }

? ? return root;

}

@end

@implementation NSObject (MethodSwizzling)

+ (void)sy_swizzleInstanceSelector:(SEL)origSelector

? ? ? ? ? ? ? ? ? swizzleSelector:(SEL)swizzleSelector {

? ? ? Method origMethod = class_getInstanceMethod(self, origSelector);

? ? ? Method swizzleMethod = class_getInstanceMethod(self, swizzleSelector);

? ? ? ? BOOL isAdd = class_addMethod(self, origSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));

? ? ? if (!isAdd) {

? ? method_exchangeImplementations(origMethod, swizzleMethod);

? ? }else {

? ? ? ? class_replaceMethod(self, swizzleSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));

? ? }

}

@end

只需要在不需要檢查的方法中設(shè)置屬性為 YES 就好了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容