电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

IOS內存泄漏檢查方法及重寫MLeakFinder

瀏覽:5日期:2022-09-16 16:21:09

對于iOS開發來講,內存泄漏的問題,已經是老生常談的話題。在日常的面試中經常會提到這些問題。我們日常的開發過程中進行內存泄漏的檢測,一般是使用instrument工具中的Leaks/Allocation來進行排查,網絡上也有比較高效又好用的內存泄漏檢測工具,MLeakFinder。

MLeakFinder-原理

首先看UIViewController,當一個UIViewController被pop或dismiss的時候,這個VC包括在這個VC上的View,或者子View都會很快的被釋放。所以我們我們需要在UIViewController被POP或dismiss后一小段時間后,在這個VC上的view,subView等是否還存在。

在UIViewController+MemoryLeak.h的load方法中可以看到,早+load方法中通過runtime交換了viewWillAppear,viewDidAppear,dismissViewControllerAnimated:completion:這三個方法。

1,首先看viewWillAppear

- (void)swizzled_viewWillAppear:(BOOL)animated { [self swizzled_viewWillAppear:animated]; objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN);}

當VC進來的時候,添加關聯對象,并標記為NO

2,在看viewDidAppear

- (void)swizzled_viewDidDisappear:(BOOL)animated { [self swizzled_viewDidDisappear:animated]; if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]) {[self willDealloc];}}

通過代碼可以看出,獲取當前關聯對象的標記,當標記為YES的時候,就會調用willDealloc。

3,我們看什么時候會被標記為YES呢?

在UINavigationController+MemoryLeak.h的popViewControllerAnimated:方法中我們可以看到

- (UIViewController *)swizzled_popViewControllerAnimated:(BOOL)animated { UIViewController *poppedViewController = [self swizzled_popViewControllerAnimated:animated]; if (!poppedViewController) {return nil; } // Detail VC in UISplitViewController is not dealloced until another detail VC is shown if (self.splitViewController &&self.splitViewController.viewControllers.firstObject == self &&self.splitViewController == poppedViewController.splitViewController) {objc_setAssociatedObject(self, kPoppedDetailVCKey, poppedViewController, OBJC_ASSOCIATION_RETAIN)return poppedViewController; } // VC is not dealloced until disappear when popped using a left-edge swipe gesture extern const void *const kHasBeenPoppedKey; objc_setAssociatedObject(poppedViewController, kHasBeenPoppedKey, @(YES), OBJC_ASSOCIATION_RETAIN); return poppedViewController;}

我們可以看出,在VC被pop或者左滑返回的時候,相當于視圖銷毀,就會被標記為YES。

4,我們重點看willDealloc

- (BOOL)willDealloc { //第一步 NSString *className = NSStringFromClass([self class]); if ([[NSObject classNamesWhitelist] containsObject:className])return NO; //第二步 NSNumber *senderPtr = objc_getAssociatedObject([UIApplication sharedApplication], kLatestSenderKey) if ([senderPtr isEqualToNumber:@((uintptr_t)self)])return NO; //第三步 __weak id weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{__strong id strongSelf = weakSelf;[strongSelf assertNotDealloc]; }); return YES;}

1.第一步:我們可以看到,會先判斷當前的class是否在白名單中,是的話就會return NO,即不是內存泄漏的。同時我們查看構建白名單的源碼:使用了一個單例實現,確保只有一個,是個私有方法

+ (NSMutableSet *)classNamesWhitelist { static NSMutableSet *whitelist = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{whitelist = [NSMutableSet setWithObjects: @'UIFieldEditor', // UIAlertControllerTextField @'UINavigationBar', @'_UIAlertControllerActionView', @'_UIVisualEffectBackdropView', nil];// System’s bug since iOS 10 and not fixed yet up to this ci.NSString *systemVersion = [UIDevice currentDevice].systemVersion;if ([systemVersion compare:@'10.0' options:NSNumericSearch] != NSOrderedAscending) { [whitelist addObject:@'UISwitch'];} }); return whitelist;}

同時在還支持,自定義的添加白名單

+ (void)addClassNamesToWhitelist:(NSArray *)classNames { [[self classNamesWhitelist] addObjectsFromArray:classNames];}

2. 第二步:判斷該對象是否是上一次發送action的對象,是的話,不進行內存檢測

//第二步 NSNumber *senderPtr = objc_getAssociatedObject([UIApplication sharedApplication], kLatestSenderKey) if ([senderPtr isEqualToNumber:@((uintptr_t)self)])return NO;

3,第三步:弱指針指向self,2s延遲,然后通過這個弱指針調用-assertNotDealloc,若被釋放,給nil發消息直接返回,不觸發-assertNotDealloc方法,認為已經釋放;如果它沒有被釋放(泄漏了),-assertNotDealloc就會被調用

__weak id weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{__strong id strongSelf = weakSelf;[strongSelf assertNotDealloc]; });5,現在我們回到:2的代碼 [self willDealloc]

看一下他的源碼

- (BOOL)willDealloc { //第一步 if (![super willDealloc]) {return NO; } //第二步 [self willReleaseChildren:self.childViewControllers]; [self willReleaseChild:self.presentedViewController]; if (self.isViewLoaded) {[self willReleaseChild:self.view]; } return YES;}

1,第一步:會通過 super調用父類的willDealloc,即上面目錄4

2,第二步:調用willReleaseChildren,willReleaseChild遍歷該對象的子對象,看其是否釋放

- (void)willReleaseChild:(id)child { if (!child) {return; }[self willReleaseChildren:@[ child ]];}- (void)willReleaseChildren:(NSArray *)children { NSArray *viewStack = [self viewStack]; NSSet *parentPtrs = [self parentPtrs]; for (id child in children) {NSString *className = NSStringFromClass([child class]);[child setViewStack:[viewStack arrayByAddingObject:className]];[child setParentPtrs:[parentPtrs setByAddingObject:@((uintptr_t)child)]];[child willDealloc]; }}

通過代碼可以看出,通過調用willReleaseChildren的方法,獲取當前對象viewStack,parentPtrs,并且遍歷children,為每個子對象設置viewStack,parentPtrs,然后調用willDealloc。

通過源碼看一下viewStask,parentPtrs的實現

- (NSArray *)viewStack { NSArray *viewStack = objc_getAssociatedObject(self, kViewStackKey); if (viewStack) {return viewStack; }NSString *className = NSStringFromClass([self class]); return @[ className ];}- (void)setViewStack:(NSArray *)viewStack { objc_setAssociatedObject(self, kViewStackKey, viewStack, OBJC_ASSOCIATION_RETAIN);}- (NSSet *)parentPtrs { NSSet *parentPtrs = objc_getAssociatedObject(self, kParentPtrsKey); if (!parentPtrs) {parentPtrs = [[NSSet alloc] initWithObjects:@((uintptr_t)self), nil]; } return parentPtrs;}- (void)setParentPtrs:(NSSet *)parentPtrs { objc_setAssociatedObject(self, kParentPtrsKey, parentPtrs, OBJC_ASSOCIATION_RETAIN);}

viewStack使用數組,parentPtrs使用的集合形式。都是通過運行時,用關聯對象添加屬性。

parentPtrs會在-assertNotDealloc中,會判斷當前對象是否與父節點集合有交集。下面仔細看下-assertNotDealloc方法

- (void)assertNotDealloc { //第一步 if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]) {return; } //第二步 [MLeakedObjectProxy addLeakedObject:self];NSString *className = NSStringFromClass([self class]); NSLog(@'Possibly Memory Leak.nIn case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.nView-ViewController stack: %@', className, className, [self viewStack]);}

1,第一步我們看到,通過parentPtrs的判斷是否有交集

產看其源碼:

+ (BOOL)isAnyObjectLeakedAtPtrs:(NSSet *)ptrs { NSAssert([NSThread isMainThread], @'Must be in main thread.'); static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{leakedObjectPtrs = [[NSMutableSet alloc] init]; }); if (!ptrs.count) {return NO } if ([leakedObjectPtrs intersectsSet:ptrs]) {return YES; } else {return NO; }}

可以看到,創建了一個單例對象,通過集合的形式,判斷是否有交集,是的話return。否則就進入第二步

2,第二步:addLeakedObject

+ (void)addLeakedObject:(id)object { NSAssert([NSThread isMainThread], @'Must be in main thread.'); MLeakedObjectProxy *proxy = [[MLeakedObjectProxy alloc] init]; proxy.object = object; proxy.objectPtr = @((uintptr_t)object); proxy.viewStack = [object viewStack]; static const void *const kLeakedObjectProxyKey = &kLeakedObjectProxyKey; objc_setAssociatedObject(object, kLeakedObjectProxyKey, proxy, OBJC_ASSOCIATION_RETAIN); [leakedObjectPtrs addObject:proxy.objectPtr];#if _INTERNAL_MLF_RC_ENABLED [MLeaksMessenger alertWithTitle:@'Memory Leak' message:[NSString stringWithFormat:@'%@', proxy.viewStack] delegate:proxy additionalButtonTitle:@'Retain Cycle'];#else [MLeaksMessenger alertWithTitle:@'Memory Leak' message:[NSString stringWithFormat:@'%@', proxy.viewStack]];#endif}

第一步:構造MLeakedObjectProxy對象,給傳入的泄漏對象 object 關聯一個代理即 proxy

第二步:通過objc_setAssociatedObject(object, kLeakedObjectProxyKey, proxy, OBJC_ASSOCIATION_RETAIN)方法,object強持有proxy, proxy若持有object,如果object釋放,proxy也會釋放

第三步:存儲 proxy.objectPtr(實際是對象地址)到集合 leakedObjectPtrs 里邊

第四步:彈框 AlertView若 _INTERNAL_MLF_RC_ENABLED == 1,則彈框會增加檢測循環引用的選項;若 _INTERNAL_MLF_RC_ENABLED == 0,則僅展示堆棧信息。

對于MLeakedObjectProxy類而言,是檢測到內存泄漏才產生的,作為泄漏對象的屬性存在的,如果泄漏的對象被釋放,那么MLeakedObjectProxy也會被釋放,則調用-dealloc函數

集合leakedObjectPtrs中移除該對象地址,同時再次彈窗,提示該對象已經釋放了

6,自己也在嘗試重寫該框架,歡迎大家一起交流

以上就是IOS內存泄漏檢查方法及重寫MLeakFinder的詳細內容,更多關于IOS內存泄漏的資料請關注好吧啦網其它相關文章!

標簽: IOS
相關文章:
主站蜘蛛池模板: 能耗监测系统-节能监测系统-能源管理系统-三水智能化 | 手表腕表维修保养鉴定售后服务中心网点 - 名表维修保养 | 许昌奥仕达自动化设备有限公司| 昆山PCB加工_SMT贴片_PCB抄板_线路板焊接加工-昆山腾宸电子科技有限公司 | 万师讲师网-优质讲师培训师供应商,讲师认证,找讲师来万师 | 北京亦庄厂房出租_经开区产业园招商信息平台 | 电缆接头-防爆电缆接头-格兰头-金属电缆接头-防爆填料函 | 高通量组织研磨仪-多样品组织研磨仪-全自动组织研磨仪-研磨者科技(广州)有限公司 | 安徽成考网-安徽成人高考网| 航空连接器,航空插头,航空插座,航空接插件,航插_深圳鸿万科 | 胶原检测试剂盒,弹性蛋白检测试剂盒,类克ELISA试剂盒,阿达木单抗ELISA试剂盒-北京群晓科苑生物技术有限公司 | 衬氟止回阀_衬氟闸阀_衬氟三通球阀_衬四氟阀门_衬氟阀门厂-浙江利尔多阀门有限公司 | 硅PU球场、篮球场地面施工「水性、环保、弹性」硅PU材料生产厂家-广东中星体育公司 | 山西3A认证|太原AAA信用认证|投标AAA信用证书-山西AAA企业信用评级网 | 全温恒温摇床-水浴气浴恒温摇床-光照恒温培养摇床-常州金坛精达仪器制造有限公司 | 3D全息投影_地面互动投影_360度立体投影_水幕灯光秀 | 广东银虎 蜂窝块状沸石分子筛-吸附脱硫分子筛-萍乡市捷龙环保科技有限公司 | 德州万泰装饰 - 万泰装饰装修设计软装家居馆 | 钢衬玻璃厂家,钢衬玻璃管道 -山东东兴扬防腐设备有限公司 | 北京银联移动POS机办理_收银POS机_智能pos机_刷卡机_收银系统_个人POS机-谷骐科技【官网】 | LED显示屏_LED屏方案设计精准报价专业安装丨四川诺显科技 | 标准光源箱|对色灯箱|色差仪|光泽度仪|涂层测厚仪_HRC大品牌生产厂家 | 超细粉碎机|超微气流磨|气流分级机|粉体改性设备|超微粉碎设备-山东埃尔派粉碎机厂家 | 北京租车牌|京牌指标租赁|小客车指标出租 | 邢台人才网_邢台招聘网_邢台123招聘【智达人才网】 | sfp光模块,高速万兆光模块工厂-性价比更高的光纤模块制造商-武汉恒泰通 | 黑龙江京科脑康医院-哈尔滨精神病医院哪家好_哈尔滨精神科医院排名_黑龙江精神心理病专科医院 | 工业铝型材生产厂家_铝合金型材配件批发精加工定制厂商 - 上海岐易铝业 | 微波消解仪器_智能微波消解仪报价_高压微波消解仪厂家_那艾 | 不锈钢螺丝 - 六角螺丝厂家 - 不锈钢紧固件 - 万千紧固件--紧固件一站式采购 | 玉米深加工机械,玉米加工设备,玉米加工机械等玉米深加工设备制造商-河南成立粮油机械有限公司 | 福州仿石漆加盟_福建仿石漆厂家-外墙仿石漆加盟推荐铁壁金钢(福建)新材料科技有限公司有保障 | 知企服务-企业综合服务(ZiKeys.com)-品优低价、种类齐全、过程管理透明、速度快捷高效、放心服务,知企专家! | 焊接烟尘净化器__焊烟除尘设备_打磨工作台_喷漆废气治理设备 -催化燃烧设备 _天津路博蓝天环保科技有限公司 | 贵州自考_贵州自学考试网| 聚氨酯保温钢管_聚氨酯直埋保温管道_聚氨酯发泡保温管厂家-沧州万荣防腐保温管道有限公司 | 颚式破碎机,圆锥破碎机,制砂机-新乡市德诚机电制造有限公司 | 沈阳液压泵_沈阳液压阀_沈阳液压站-沈阳海德太科液压设备有限公司 | 超细粉碎机|超微气流磨|气流分级机|粉体改性设备|超微粉碎设备-山东埃尔派粉碎机厂家 | 济南ISO9000认证咨询代理公司,ISO9001认证,CMA实验室认证,ISO/TS16949认证,服务体系认证,资产管理体系认证,SC食品生产许可证- 济南创远企业管理咨询有限公司 郑州电线电缆厂家-防火|低压|低烟无卤电缆-河南明星电缆 | 流量检测仪-气密性检测装置-密封性试验仪-东莞市奥图自动化科技有限公司 | 深圳天际源广告-形象堆头,企业文化墙,喷绘,门头招牌设计制作专家 |