├── MLeaksFinder.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── Podfile.lock ├── MLeaksFinder.xcworkspace └── contents.xcworkspacedata ├── Podfile ├── FAQ-CN.md ├── MLeaksFinder ├── UITouch+MemoryLeak.h ├── UIView+MemoryLeak.h ├── UIApplication+MemoryLeak.h ├── UIViewController+MemoryLeak.h ├── UIPageViewController+MemoryLeak.h ├── UITabBarController+MemoryLeak.h ├── UINavigationController+MemoryLeak.h ├── UISplitViewController+MemoryLeak.h ├── MLeakedObjectProxy.h ├── UIView+MemoryLeak.m ├── MLeaksMessenger.h ├── UITabBarController+MemoryLeak.m ├── UIPageViewController+MemoryLeak.m ├── UISplitViewController+MemoryLeak.m ├── MLeaksFinder.h ├── NSObject+MemoryLeak.h ├── UITouch+MemoryLeak.m ├── UIApplication+MemoryLeak.m ├── MLeaksMessenger.m ├── UIViewController+MemoryLeak.m ├── UINavigationController+MemoryLeak.m ├── MLeakedObjectProxy.m └── NSObject+MemoryLeak.m ├── .gitignore ├── MLeaksFinder.podspec ├── LICENSE.TXT └── README.md /MLeaksFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FBRetainCycleDetector (0.1.2) 3 | 4 | DEPENDENCIES: 5 | - FBRetainCycleDetector 6 | 7 | SPEC CHECKSUMS: 8 | FBRetainCycleDetector: 45462f9ed1af0cd77224a4cc4e6dacaca47371b4 9 | 10 | PODFILE CHECKSUM: 38741d1572d2c8740a441bc6562c61fa2ce1ef2e 11 | 12 | COCOAPODS: 1.0.0 13 | -------------------------------------------------------------------------------- /MLeaksFinder.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '7.0' 3 | 4 | target 'MLeaksFinder' do 5 | # Uncomment this line if you're using Swift or would like to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for MLeaksFinder 9 | pod 'FBRetainCycleDetector' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /FAQ-CN.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | **1) 引进 MLeaksFinder 后没生效?** 3 | 4 | * 先验证引进是否正确,在 UIViewController+MemoryLeak.m 的 `+ (void)load` 方法里加断点,app 启动时进入该方法则引进成功,否则引进失败。 5 | * 用 CocoaPods 安装时注意有没有 warnings,特别是 `OTHER_LDFLAGS` 相关的 warnings。如果有 warnings,可以在主工程的 Build Settings -> Other Linker Flags 加上 `-ObjC`。 6 | 7 | **2) 可以手动引进 MLeaksFinder 吗?** 8 | 9 | * 直接把 MLeaksFinder 的代码放到项目里即生效。如果把 MLeaksFinder 做为子工程,需要在主工程的 Build Settings -> Other Linker Flags 加上 `-ObjC`。 10 | * 引进 MLeaksFinder 的代码后即可检测内存泄漏,但查找循环引用的功能还未生效。可以再手动加入 FBRetainCycleDetector 代码,然后把 MLeaksFinder.h 里的 `//#define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 1` 打开。 11 | 12 | **3) Fail to find a retain cycle?** 13 | 14 | * 内存泄漏不一定是循环引用造成的。 15 | * 有的循环引用 FBRetainCycleDetector 不一定能找出。 16 | 17 | **4) 如何关掉 MLeaksFinder?** 18 | 19 | * MLeaksFinder 默认只在 debug 下生效,当然也可以通过 MLeaksFinder.h 里的 `//#define MEMORY_LEAKS_FINDER_ENABLED 0` 来手动控制开关。 20 | -------------------------------------------------------------------------------- /MLeaksFinder/UITouch+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UITouch (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UIView+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UIView (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UIApplication+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UIApplication (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UIViewController+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UIViewController (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UIPageViewController+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UIPageViewController (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UITabBarController+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UITabBarController (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UINavigationController+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UINavigationController (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/UISplitViewController+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import "MLeaksFinder.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @interface UISplitViewController (MemoryLeak) 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /MLeaksFinder/MLeakedObjectProxy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | 15 | @interface MLeakedObjectProxy : NSObject 16 | 17 | + (BOOL)isAnyObjectLeakedAtPtrs:(NSSet *)ptrs; 18 | + (void)addLeakedObject:(id)object; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MLeaksFinder/UIView+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UIView+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @implementation UIView (MemoryLeak) 19 | 20 | - (BOOL)willDealloc { 21 | if (![super willDealloc]) { 22 | return NO; 23 | } 24 | 25 | [self willReleaseChildren:self.subviews]; 26 | 27 | return YES; 28 | } 29 | 30 | @end 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MLeaksFinder/MLeaksMessenger.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | #import 15 | 16 | @interface MLeaksMessenger : NSObject 17 | 18 | + (void)alertWithTitle:(NSString *)title message:(NSString *)message; 19 | + (void)alertWithTitle:(NSString *)title 20 | message:(NSString *)message 21 | delegate:(id)delegate 22 | additionalButtonTitle:(NSString *)additionalButtonTitle; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /MLeaksFinder/UITabBarController+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UITabBarController+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @implementation UITabBarController (MemoryLeak) 19 | 20 | - (BOOL)willDealloc { 21 | if (![super willDealloc]) { 22 | return NO; 23 | } 24 | 25 | [self willReleaseChildren:self.viewControllers]; 26 | 27 | return YES; 28 | } 29 | 30 | @end 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MLeaksFinder/UIPageViewController+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UIPageViewController+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @implementation UIPageViewController (MemoryLeak) 19 | 20 | - (BOOL)willDealloc { 21 | if (![super willDealloc]) { 22 | return NO; 23 | } 24 | 25 | [self willReleaseChildren:self.viewControllers]; 26 | 27 | return YES; 28 | } 29 | 30 | @end 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MLeaksFinder/UISplitViewController+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UISplitViewController+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | @implementation UISplitViewController (MemoryLeak) 19 | 20 | - (BOOL)willDealloc { 21 | if (![super willDealloc]) { 22 | return NO; 23 | } 24 | 25 | [self willReleaseChildren:self.viewControllers]; 26 | 27 | return YES; 28 | } 29 | 30 | @end 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MLeaksFinder/MLeaksFinder.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "NSObject+MemoryLeak.h" 14 | 15 | //#define MEMORY_LEAKS_FINDER_ENABLED 0 16 | 17 | #ifdef MEMORY_LEAKS_FINDER_ENABLED 18 | #define _INTERNAL_MLF_ENABLED MEMORY_LEAKS_FINDER_ENABLED 19 | #else 20 | #define _INTERNAL_MLF_ENABLED DEBUG 21 | #endif 22 | 23 | #define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 0 24 | 25 | #ifdef MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 26 | #define _INTERNAL_MLF_RC_ENABLED MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 27 | #elif COCOAPODS 28 | #define _INTERNAL_MLF_RC_ENABLED COCOAPODS 29 | #endif 30 | -------------------------------------------------------------------------------- /MLeaksFinder/NSObject+MemoryLeak.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import 14 | 15 | #define MLCheck(TARGET) [self willReleaseObject:(TARGET) relationship:@#TARGET]; 16 | 17 | @protocol MLeaksFinderDelegate 18 | 19 | - (BOOL)viewController:(UIViewController *)viewController shouldCheckProperty:(NSString *)propertyName; 20 | 21 | @end 22 | 23 | @interface NSObject (MemoryLeak) 24 | 25 | - (BOOL)willDealloc; 26 | - (void)willReleaseObject:(id)object relationship:(NSString *)relationship; 27 | 28 | - (void)willReleaseChild:(id)child; 29 | - (void)willReleaseChildren:(NSArray *)children; 30 | 31 | - (NSArray *)viewStack; 32 | 33 | + (void)setDelegate:(id)delegate; 34 | 35 | + (void)addClassNamesToWhitelist:(NSArray *)classNames; 36 | 37 | + (void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /MLeaksFinder/UITouch+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UITouch+MemoryLeak.h" 14 | #import 15 | 16 | #if _INTERNAL_MLF_ENABLED 17 | 18 | extern const void *const kLatestSenderKey; 19 | 20 | @implementation UITouch (MemoryLeak) 21 | 22 | + (void)load { 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | [self swizzleSEL:@selector(setView:) withSEL:@selector(swizzled_setView:)]; 26 | }); 27 | } 28 | 29 | - (void)swizzled_setView:(UIView *)view { 30 | [self swizzled_setView:view]; 31 | 32 | if (view) { 33 | objc_setAssociatedObject([UIApplication sharedApplication], 34 | kLatestSenderKey, 35 | @((uintptr_t)view), 36 | OBJC_ASSOCIATION_RETAIN); 37 | } 38 | } 39 | 40 | @end 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | # Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | 62 | .DS_Store 63 | -------------------------------------------------------------------------------- /MLeaksFinder/UIApplication+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UIApplication+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | #import 16 | 17 | #if _INTERNAL_MLF_ENABLED 18 | 19 | extern const void *const kLatestSenderKey; 20 | 21 | @implementation UIApplication (MemoryLeak) 22 | 23 | + (void)load { 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | [self swizzleSEL:@selector(sendAction:to:from:forEvent:) withSEL:@selector(swizzled_sendAction:to:from:forEvent:)]; 27 | }); 28 | } 29 | 30 | - (BOOL)swizzled_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event { 31 | objc_setAssociatedObject(self, kLatestSenderKey, @((uintptr_t)sender), OBJC_ASSOCIATION_RETAIN); 32 | 33 | return [self swizzled_sendAction:action to:target from:sender forEvent:event]; 34 | } 35 | 36 | @end 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /MLeaksFinder.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint MLeaksFinder.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "MLeaksFinder" 11 | s.version = "1.0.0" 12 | s.summary = "Find memory leaks in your iOS app at develop time." 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | # s.description = <<-DESC 21 | #TODO: Add long description of the pod here. 22 | # DESC 23 | 24 | s.homepage = "https://github.com/Zepo/MLeaksFinder" 25 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 26 | s.license = 'MIT' 27 | s.author = { "Zeposhe" => "zeposhe@163.com" } 28 | s.source = { :git => "https://github.com/Zepo/MLeaksFinder.git", :tag => s.version } 29 | # s.social_media_url = 'https://twitter.com/' 30 | 31 | s.ios.deployment_target = '6.0' 32 | 33 | s.source_files = 'MLeaksFinder/**/*' 34 | 35 | # s.resource_bundles = { 36 | # 'MLeaksFinder' => ['MLeaksFinder/Assets/*.png'] 37 | # } 38 | 39 | s.public_header_files = 'MLeaksFinder/MLeaksFinder.h', 'MLeaksFinder/NSObject+MemoryLeak.h' 40 | # s.frameworks = 'UIKit', 'MapKit' 41 | # s.dependency 'FBRetainCycleDetector' 42 | end 43 | -------------------------------------------------------------------------------- /MLeaksFinder/MLeaksMessenger.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "MLeaksMessenger.h" 14 | 15 | static __weak UIAlertView *alertView; 16 | 17 | @implementation MLeaksMessenger 18 | 19 | + (void)alertWithTitle:(NSString *)title message:(NSString *)message { 20 | [self alertWithTitle:title message:message delegate:nil additionalButtonTitle:nil]; 21 | } 22 | 23 | + (void)alertWithTitle:(NSString *)title 24 | message:(NSString *)message 25 | delegate:(id)delegate 26 | additionalButtonTitle:(NSString *)additionalButtonTitle { 27 | [alertView dismissWithClickedButtonIndex:0 animated:NO]; 28 | UIAlertView *alertViewTemp = [[UIAlertView alloc] initWithTitle:title 29 | message:message 30 | delegate:delegate 31 | cancelButtonTitle:@"OK" 32 | otherButtonTitles:additionalButtonTitle, nil]; 33 | [alertViewTemp show]; 34 | alertView = alertViewTemp; 35 | 36 | NSLog(@"%@: %@", title, message); 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Tencent is pleased to support the open source community by making MLeaksFinder available. 2 | Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 3 | If you have downloaded a copy of the MLeaksFinder binary from Tencent, please note that the MLeaksFinder binary is licensed under the BSD 3-Clause License. 4 | If you have downloaded a copy of the MLeaksFinder source code from Tencent, please note that MLeaksFinder source code is licensed under the BSD 3-Clause License. Your integration of MLeaksFinder into your own projects may require compliance with the BSD 3-Clause License. 5 | A copy of the BSD 3-Clause License is included in this file. 6 | 7 | 8 | 9 | Terms of the BSD 3-Clause License: 10 | -------------------------------------------------------------------- 11 | 12 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 13 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 14 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 15 | Neither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [中文介绍](http://wereadteam.github.io/2016/07/20/MLeaksFinder2/) | [FAQ中文](https://github.com/Zepo/MLeaksFinder/blob/master/FAQ-CN.md) 2 | 3 | # MLeaksFinder 4 | MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIView and UIViewController objects, present an alert with the leaked object in its View-ViewController stack when leaks happening. ~~More over, it can try to find a retain cycle for the leaked object using [FBRetainCycleDetector](https://github.com/facebook/FBRetainCycleDetector/tree/master/FBRetainCycleDetector).~~ Besides finding leaks in UIView and UIViewController objects, developers can extend it to find leaks in other kinds of objects. 5 | 6 | # Communication 7 | QQ group: 482121244 8 | 9 | # Installation 10 | ``` 11 | pod 'MLeaksFinder' 12 | ``` 13 | MLeaksFinder comes into effect after `pod install`, there is no need to add any code nor to import any header file. 14 | 15 | *WARNING: FBRetainCycleDetector is removed from the podspec due to Facebook's BSD-plus-Patents license. If you want to use FBRetainCycleDetector to find retain cycle, add `pod 'FBRetainCycleDetector'` to your project's Podfile and turn the macro `MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED` on in `MLeaksFinder.h`.* 16 | 17 | # Usage 18 | MLeaksFinder can automatically find leaks in UIView and UIViewController objects. When leaks happening, it will present an alert with the leaked object in its View-ViewController stack. 19 | ``` 20 | Memory Leak 21 | ( 22 | MyTableViewController, 23 | UITableView, 24 | UITableViewWrapperView, 25 | MyTableViewCell 26 | ) 27 | ``` 28 | 29 | For the above example, we are sure that objects of `MyTableViewController`, `UITableView`, `UITableViewWrapperView` are deallocated successfully, but not the objects of `MyTableViewCell`. 30 | 31 | ## Mute Assertion 32 | If your class is designed as singleton or for some reason objects of your class should not be dealloced, override `- (BOOL)willDealloc` in your class by returning NO. 33 | ```objc 34 | - (BOOL)willDealloc { 35 | return NO; 36 | } 37 | ``` 38 | 39 | ## Find Leaks in Other Objects 40 | MLeaksFinder finds leaks in UIView and UIViewController objects by default. However, you can extend it to find leaks in the whole object graph rooted at a UIViewController object. 41 | ```objc 42 | - (BOOL)willDealloc { 43 | if (![super willDealloc]) { 44 | return NO; 45 | } 46 | 47 | MLCheck(self.viewModel); 48 | return YES; 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /MLeaksFinder/UIViewController+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UIViewController+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | #import 16 | 17 | #if _INTERNAL_MLF_ENABLED 18 | 19 | const void *const kHasBeenPoppedKey = &kHasBeenPoppedKey; 20 | extern const void *const kDelegateKey; 21 | 22 | @implementation UIViewController (MemoryLeak) 23 | 24 | + (void)load { 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | [self swizzleSEL:@selector(viewDidDisappear:) withSEL:@selector(swizzled_viewDidDisappear:)]; 28 | [self swizzleSEL:@selector(viewWillAppear:) withSEL:@selector(swizzled_viewWillAppear:)]; 29 | [self swizzleSEL:@selector(dismissViewControllerAnimated:completion:) withSEL:@selector(swizzled_dismissViewControllerAnimated:completion:)]; 30 | }); 31 | } 32 | 33 | - (void)swizzled_viewDidDisappear:(BOOL)animated { 34 | [self swizzled_viewDidDisappear:animated]; 35 | 36 | if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]) { 37 | [self willDealloc]; 38 | } 39 | } 40 | 41 | - (void)swizzled_viewWillAppear:(BOOL)animated { 42 | [self swizzled_viewWillAppear:animated]; 43 | 44 | objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN); 45 | } 46 | 47 | - (void)swizzled_dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { 48 | [self swizzled_dismissViewControllerAnimated:flag completion:completion]; 49 | 50 | UIViewController *dismissedViewController = self.presentedViewController; 51 | if (!dismissedViewController && self.presentingViewController) { 52 | dismissedViewController = self; 53 | } 54 | 55 | if (!dismissedViewController) return; 56 | 57 | [dismissedViewController willDealloc]; 58 | } 59 | 60 | - (BOOL)willDealloc { 61 | if (![super willDealloc]) { 62 | return NO; 63 | } 64 | 65 | [self willReleaseChildren:self.childViewControllers]; 66 | [self willReleaseChild:self.presentedViewController]; 67 | 68 | if (self.isViewLoaded) { 69 | [self willReleaseChild:self.view]; 70 | } 71 | 72 | unsigned int count; 73 | Ivar *ivars = class_copyIvarList([self class], &count); 74 | for (unsigned int i = 0; i < count; ++i) { 75 | Ivar ivar = ivars[i]; 76 | NSString *ivarName = @(ivar_getName(ivar)); 77 | NSString *ivarType = @(ivar_getTypeEncoding(ivar)); 78 | if (![ivarType hasPrefix:@"@"]) { 79 | continue; 80 | } 81 | id delegate = objc_getAssociatedObject([NSObject class], kDelegateKey); 82 | if (delegate && [delegate viewController:self shouldCheckProperty:ivarName]) { 83 | id ivarObject = object_getIvar(self, ivar); 84 | if (ivarObject) { 85 | [self willReleaseChild:ivarObject]; 86 | } 87 | } 88 | } 89 | free(ivars); 90 | 91 | return YES; 92 | } 93 | 94 | @end 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /MLeaksFinder/UINavigationController+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "UINavigationController+MemoryLeak.h" 14 | #import "NSObject+MemoryLeak.h" 15 | #import 16 | 17 | #if _INTERNAL_MLF_ENABLED 18 | 19 | static const void *const kPoppedDetailVCKey = &kPoppedDetailVCKey; 20 | 21 | @implementation UINavigationController (MemoryLeak) 22 | 23 | + (void)load { 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | [self swizzleSEL:@selector(pushViewController:animated:) withSEL:@selector(swizzled_pushViewController:animated:)]; 27 | [self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(swizzled_popViewControllerAnimated:)]; 28 | [self swizzleSEL:@selector(popToViewController:animated:) withSEL:@selector(swizzled_popToViewController:animated:)]; 29 | [self swizzleSEL:@selector(popToRootViewControllerAnimated:) withSEL:@selector(swizzled_popToRootViewControllerAnimated:)]; 30 | }); 31 | } 32 | 33 | - (void)swizzled_pushViewController:(UIViewController *)viewController animated:(BOOL)animated { 34 | if (self.splitViewController) { 35 | id detailViewController = objc_getAssociatedObject(self, kPoppedDetailVCKey); 36 | if ([detailViewController isKindOfClass:[UIViewController class]]) { 37 | [detailViewController willDealloc]; 38 | objc_setAssociatedObject(self, kPoppedDetailVCKey, nil, OBJC_ASSOCIATION_RETAIN); 39 | } 40 | } 41 | 42 | [self swizzled_pushViewController:viewController animated:animated]; 43 | } 44 | 45 | - (UIViewController *)swizzled_popViewControllerAnimated:(BOOL)animated { 46 | UIViewController *poppedViewController = [self swizzled_popViewControllerAnimated:animated]; 47 | 48 | if (!poppedViewController) { 49 | return nil; 50 | } 51 | 52 | // Detail VC in UISplitViewController is not dealloced until another detail VC is shown 53 | if (self.splitViewController && 54 | self.splitViewController.viewControllers.firstObject == self && 55 | self.splitViewController == poppedViewController.splitViewController) { 56 | objc_setAssociatedObject(self, kPoppedDetailVCKey, poppedViewController, OBJC_ASSOCIATION_RETAIN); 57 | return poppedViewController; 58 | } 59 | 60 | // VC is not dealloced until disappear when popped using a left-edge swipe gesture 61 | extern const void *const kHasBeenPoppedKey; 62 | objc_setAssociatedObject(poppedViewController, kHasBeenPoppedKey, @(YES), OBJC_ASSOCIATION_RETAIN); 63 | 64 | return poppedViewController; 65 | } 66 | 67 | - (NSArray *)swizzled_popToViewController:(UIViewController *)viewController animated:(BOOL)animated { 68 | NSArray *poppedViewControllers = [self swizzled_popToViewController:viewController animated:animated]; 69 | 70 | for (UIViewController *viewController in poppedViewControllers) { 71 | [viewController willDealloc]; 72 | } 73 | 74 | return poppedViewControllers; 75 | } 76 | 77 | - (NSArray *)swizzled_popToRootViewControllerAnimated:(BOOL)animated { 78 | NSArray *poppedViewControllers = [self swizzled_popToRootViewControllerAnimated:animated]; 79 | 80 | for (UIViewController *viewController in poppedViewControllers) { 81 | [viewController willDealloc]; 82 | } 83 | 84 | return poppedViewControllers; 85 | } 86 | 87 | - (BOOL)willDealloc { 88 | if (![super willDealloc]) { 89 | return NO; 90 | } 91 | 92 | [self willReleaseChildren:self.viewControllers]; 93 | 94 | return YES; 95 | } 96 | 97 | @end 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /MLeaksFinder/MLeakedObjectProxy.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "MLeakedObjectProxy.h" 14 | #import "MLeaksFinder.h" 15 | #import "MLeaksMessenger.h" 16 | #import "NSObject+MemoryLeak.h" 17 | #import 18 | #import 19 | 20 | #if _INTERNAL_MLF_RC_ENABLED 21 | #import 22 | #endif 23 | 24 | static NSMutableSet *leakedObjectPtrs; 25 | 26 | @interface MLeakedObjectProxy () 27 | @property (nonatomic, weak) id object; 28 | @property (nonatomic, strong) NSNumber *objectPtr; 29 | @property (nonatomic, strong) NSArray *viewStack; 30 | @end 31 | 32 | @implementation MLeakedObjectProxy 33 | 34 | + (BOOL)isAnyObjectLeakedAtPtrs:(NSSet *)ptrs { 35 | NSAssert([NSThread isMainThread], @"Must be in main thread."); 36 | 37 | static dispatch_once_t onceToken; 38 | dispatch_once(&onceToken, ^{ 39 | leakedObjectPtrs = [[NSMutableSet alloc] init]; 40 | }); 41 | 42 | if (!ptrs.count) { 43 | return NO; 44 | } 45 | if ([leakedObjectPtrs intersectsSet:ptrs]) { 46 | return YES; 47 | } else { 48 | return NO; 49 | } 50 | } 51 | 52 | + (void)addLeakedObject:(id)object { 53 | NSAssert([NSThread isMainThread], @"Must be in main thread."); 54 | 55 | MLeakedObjectProxy *proxy = [[MLeakedObjectProxy alloc] init]; 56 | proxy.object = object; 57 | proxy.objectPtr = @((uintptr_t)object); 58 | proxy.viewStack = [object viewStack]; 59 | static const void *const kLeakedObjectProxyKey = &kLeakedObjectProxyKey; 60 | objc_setAssociatedObject(object, kLeakedObjectProxyKey, proxy, OBJC_ASSOCIATION_RETAIN); 61 | 62 | [leakedObjectPtrs addObject:proxy.objectPtr]; 63 | 64 | #if _INTERNAL_MLF_RC_ENABLED 65 | [MLeaksMessenger alertWithTitle:@"Memory Leak" 66 | message:[NSString stringWithFormat:@"%@", proxy.viewStack] 67 | delegate:proxy 68 | additionalButtonTitle:@"Retain Cycle"]; 69 | #else 70 | [MLeaksMessenger alertWithTitle:@"Memory Leak" 71 | message:[NSString stringWithFormat:@"%@", proxy.viewStack]]; 72 | #endif 73 | } 74 | 75 | - (void)dealloc { 76 | NSNumber *objectPtr = _objectPtr; 77 | NSArray *viewStack = _viewStack; 78 | dispatch_async(dispatch_get_main_queue(), ^{ 79 | [leakedObjectPtrs removeObject:objectPtr]; 80 | [MLeaksMessenger alertWithTitle:@"Object Deallocated" 81 | message:[NSString stringWithFormat:@"%@", viewStack]]; 82 | }); 83 | } 84 | 85 | #pragma mark - UIAlertViewDelegate 86 | 87 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { 88 | if (!buttonIndex) { 89 | return; 90 | } 91 | 92 | id object = self.object; 93 | if (!object) { 94 | return; 95 | } 96 | 97 | #if _INTERNAL_MLF_RC_ENABLED 98 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 99 | FBRetainCycleDetector *detector = [FBRetainCycleDetector new]; 100 | [detector addCandidate:self.object]; 101 | NSSet *retainCycles = [detector findRetainCyclesWithMaxCycleLength:20]; 102 | 103 | BOOL hasFound = NO; 104 | for (NSArray *retainCycle in retainCycles) { 105 | NSInteger index = 0; 106 | for (FBObjectiveCGraphElement *element in retainCycle) { 107 | if (element.object == object) { 108 | NSArray *shiftedRetainCycle = [self shiftArray:retainCycle toIndex:index]; 109 | 110 | dispatch_async(dispatch_get_main_queue(), ^{ 111 | [MLeaksMessenger alertWithTitle:@"Retain Cycle" 112 | message:[NSString stringWithFormat:@"%@", shiftedRetainCycle]]; 113 | }); 114 | hasFound = YES; 115 | break; 116 | } 117 | 118 | ++index; 119 | } 120 | if (hasFound) { 121 | break; 122 | } 123 | } 124 | if (!hasFound) { 125 | dispatch_async(dispatch_get_main_queue(), ^{ 126 | [MLeaksMessenger alertWithTitle:@"Retain Cycle" 127 | message:@"Fail to find a retain cycle"]; 128 | }); 129 | } 130 | }); 131 | #endif 132 | } 133 | 134 | - (NSArray *)shiftArray:(NSArray *)array toIndex:(NSInteger)index { 135 | if (index == 0) { 136 | return array; 137 | } 138 | 139 | NSRange range = NSMakeRange(index, array.count - index); 140 | NSMutableArray *result = [[array subarrayWithRange:range] mutableCopy]; 141 | [result addObjectsFromArray:[array subarrayWithRange:NSMakeRange(0, index)]]; 142 | return result; 143 | } 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /MLeaksFinder/NSObject+MemoryLeak.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Tencent is pleased to support the open source community by making MLeaksFinder available. 3 | * 4 | * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | #import "NSObject+MemoryLeak.h" 14 | #import "MLeakedObjectProxy.h" 15 | #import "MLeaksFinder.h" 16 | #import 17 | #import 18 | 19 | #if _INTERNAL_MLF_RC_ENABLED 20 | #import 21 | #endif 22 | 23 | static const void *const kViewStackKey = &kViewStackKey; 24 | static const void *const kParentPtrsKey = &kParentPtrsKey; 25 | const void *const kLatestSenderKey = &kLatestSenderKey; 26 | const void *const kDelegateKey = &kDelegateKey; 27 | 28 | @implementation NSObject (MemoryLeak) 29 | 30 | - (BOOL)willDealloc { 31 | NSString *className = NSStringFromClass([self class]); 32 | if ([[NSObject classNamesWhitelist] containsObject:className]) 33 | return NO; 34 | 35 | NSNumber *senderPtr = objc_getAssociatedObject([UIApplication sharedApplication], kLatestSenderKey); 36 | if ([senderPtr isEqualToNumber:@((uintptr_t)self)]) 37 | return NO; 38 | 39 | __weak id weakSelf = self; 40 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 41 | __strong id strongSelf = weakSelf; 42 | [strongSelf assertNotDealloc]; 43 | }); 44 | 45 | return YES; 46 | } 47 | 48 | - (void)assertNotDealloc { 49 | if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]) { 50 | return; 51 | } 52 | [MLeakedObjectProxy addLeakedObject:self]; 53 | 54 | NSString *className = NSStringFromClass([self class]); 55 | NSLog(@"Possibly Memory Leak.\nIn case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.\nView-ViewController stack: %@", className, className, [self viewStack]); 56 | } 57 | 58 | - (void)willReleaseObject:(id)object relationship:(NSString *)relationship { 59 | if ([relationship hasPrefix:@"self"]) { 60 | relationship = [relationship stringByReplacingCharactersInRange:NSMakeRange(0, 4) withString:@""]; 61 | } 62 | NSString *className = NSStringFromClass([object class]); 63 | className = [NSString stringWithFormat:@"%@(%@), ", relationship, className]; 64 | 65 | [object setViewStack:[[self viewStack] arrayByAddingObject:className]]; 66 | [object setParentPtrs:[[self parentPtrs] setByAddingObject:@((uintptr_t)object)]]; 67 | [object willDealloc]; 68 | } 69 | 70 | - (void)willReleaseChild:(id)child { 71 | if (!child) { 72 | return; 73 | } 74 | 75 | [self willReleaseChildren:@[ child ]]; 76 | } 77 | 78 | - (void)willReleaseChildren:(NSArray *)children { 79 | NSArray *viewStack = [self viewStack]; 80 | NSSet *parentPtrs = [self parentPtrs]; 81 | for (id child in children) { 82 | NSString *className = NSStringFromClass([child class]); 83 | [child setViewStack:[viewStack arrayByAddingObject:className]]; 84 | [child setParentPtrs:[parentPtrs setByAddingObject:@((uintptr_t)child)]]; 85 | [child willDealloc]; 86 | } 87 | } 88 | 89 | - (NSArray *)viewStack { 90 | NSArray *viewStack = objc_getAssociatedObject(self, kViewStackKey); 91 | if (viewStack) { 92 | return viewStack; 93 | } 94 | 95 | NSString *className = NSStringFromClass([self class]); 96 | return @[ className ]; 97 | } 98 | 99 | - (void)setViewStack:(NSArray *)viewStack { 100 | objc_setAssociatedObject(self, kViewStackKey, viewStack, OBJC_ASSOCIATION_RETAIN); 101 | } 102 | 103 | - (NSSet *)parentPtrs { 104 | NSSet *parentPtrs = objc_getAssociatedObject(self, kParentPtrsKey); 105 | if (!parentPtrs) { 106 | parentPtrs = [[NSSet alloc] initWithObjects:@((uintptr_t)self), nil]; 107 | } 108 | return parentPtrs; 109 | } 110 | 111 | - (void)setParentPtrs:(NSSet *)parentPtrs { 112 | objc_setAssociatedObject(self, kParentPtrsKey, parentPtrs, OBJC_ASSOCIATION_RETAIN); 113 | } 114 | 115 | + (void)setDelegate:(id)delegate { 116 | objc_setAssociatedObject([NSObject class], kDelegateKey, delegate, OBJC_ASSOCIATION_RETAIN); 117 | } 118 | 119 | + (NSMutableSet *)classNamesWhitelist { 120 | static NSMutableSet *whitelist = nil; 121 | static dispatch_once_t onceToken; 122 | dispatch_once(&onceToken, ^{ 123 | whitelist = [NSMutableSet setWithObjects: 124 | @"UIFieldEditor", // UIAlertControllerTextField 125 | @"UINavigationBar", 126 | @"_UIAlertControllerActionView", 127 | @"_UIVisualEffectBackdropView", 128 | nil]; 129 | 130 | // System's bug since iOS 10 and not fixed yet up to this ci. 131 | NSString *systemVersion = [UIDevice currentDevice].systemVersion; 132 | if ([systemVersion compare:@"10.0" options:NSNumericSearch] != NSOrderedAscending) { 133 | [whitelist addObject:@"UISwitch"]; 134 | } 135 | }); 136 | return whitelist; 137 | } 138 | 139 | + (void)addClassNamesToWhitelist:(NSArray *)classNames { 140 | [[self classNamesWhitelist] addObjectsFromArray:classNames]; 141 | } 142 | 143 | + (void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL { 144 | #if _INTERNAL_MLF_ENABLED 145 | 146 | #if _INTERNAL_MLF_RC_ENABLED 147 | // Just find a place to set up FBRetainCycleDetector. 148 | static dispatch_once_t onceToken; 149 | dispatch_once(&onceToken, ^{ 150 | dispatch_async(dispatch_get_main_queue(), ^{ 151 | [FBAssociationManager hook]; 152 | }); 153 | }); 154 | #endif 155 | 156 | Class class = [self class]; 157 | 158 | Method originalMethod = class_getInstanceMethod(class, originalSEL); 159 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSEL); 160 | 161 | BOOL didAddMethod = 162 | class_addMethod(class, 163 | originalSEL, 164 | method_getImplementation(swizzledMethod), 165 | method_getTypeEncoding(swizzledMethod)); 166 | 167 | if (didAddMethod) { 168 | class_replaceMethod(class, 169 | swizzledSEL, 170 | method_getImplementation(originalMethod), 171 | method_getTypeEncoding(originalMethod)); 172 | } else { 173 | method_exchangeImplementations(originalMethod, swizzledMethod); 174 | } 175 | #endif 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /MLeaksFinder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B32AAB321D767E59001485FF /* UITouch+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B32AAB311D767E59001485FF /* UITouch+MemoryLeak.m */; }; 11 | B3667E4E1D38ADF6002B55DE /* MLeakedObjectProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = B3667E4D1D38ADF6002B55DE /* MLeakedObjectProxy.m */; }; 12 | B3667E721D3BA78E002B55DE /* MLeaksMessenger.m in Sources */ = {isa = PBXBuildFile; fileRef = B3667E711D3BA78E002B55DE /* MLeaksMessenger.m */; }; 13 | B367C8971C1C029500FDA5A9 /* NSObject+MemoryLeak.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3ADE17B1C1BD2F000AA4F63 /* NSObject+MemoryLeak.h */; }; 14 | B3ADE1701C1BD1D600AA4F63 /* MLeaksFinder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3ADE16F1C1BD1D600AA4F63 /* MLeaksFinder.h */; }; 15 | B3ADE17A1C1BD29600AA4F63 /* UIView+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE1791C1BD29600AA4F63 /* UIView+MemoryLeak.m */; }; 16 | B3ADE17D1C1BD2F000AA4F63 /* NSObject+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE17C1C1BD2F000AA4F63 /* NSObject+MemoryLeak.m */; }; 17 | B3ADE1801C1BD4B200AA4F63 /* UIViewController+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE17F1C1BD4B200AA4F63 /* UIViewController+MemoryLeak.m */; }; 18 | B3ADE1831C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE1821C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.m */; }; 19 | B3ADE1861C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE1851C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.m */; }; 20 | B3ADE1891C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE1881C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.m */; }; 21 | B3ADE18C1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3ADE18B1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.m */; }; 22 | B3C512C51CE3368B0032C2C8 /* UIApplication+MemoryLeak.m in Sources */ = {isa = PBXBuildFile; fileRef = B3C512C41CE3368B0032C2C8 /* UIApplication+MemoryLeak.m */; }; 23 | FE6CAD555020A33B697B5FDE /* libPods-MLeaksFinder.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C69FD6BA6E3CCAC5168D790 /* libPods-MLeaksFinder.a */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXCopyFilesBuildPhase section */ 27 | B3ADE16A1C1BD1D600AA4F63 /* CopyFiles */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = "include/$(PRODUCT_NAME)"; 31 | dstSubfolderSpec = 16; 32 | files = ( 33 | B367C8971C1C029500FDA5A9 /* NSObject+MemoryLeak.h in CopyFiles */, 34 | B3ADE1701C1BD1D600AA4F63 /* MLeaksFinder.h in CopyFiles */, 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 5E297F00748C1FFA737EEFB0 /* Pods-MLeaksFinder.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MLeaksFinder.release.xcconfig"; path = "Pods/Target Support Files/Pods-MLeaksFinder/Pods-MLeaksFinder.release.xcconfig"; sourceTree = ""; }; 42 | 6C69FD6BA6E3CCAC5168D790 /* libPods-MLeaksFinder.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MLeaksFinder.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 90EF182415984ED6E908B442 /* Pods-MLeaksFinder.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MLeaksFinder.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MLeaksFinder/Pods-MLeaksFinder.debug.xcconfig"; sourceTree = ""; }; 44 | B32AAB301D767E59001485FF /* UITouch+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITouch+MemoryLeak.h"; sourceTree = ""; }; 45 | B32AAB311D767E59001485FF /* UITouch+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITouch+MemoryLeak.m"; sourceTree = ""; }; 46 | B3667E4C1D38ADF6002B55DE /* MLeakedObjectProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLeakedObjectProxy.h; sourceTree = ""; }; 47 | B3667E4D1D38ADF6002B55DE /* MLeakedObjectProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLeakedObjectProxy.m; sourceTree = ""; }; 48 | B3667E701D3BA78E002B55DE /* MLeaksMessenger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLeaksMessenger.h; sourceTree = ""; }; 49 | B3667E711D3BA78E002B55DE /* MLeaksMessenger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLeaksMessenger.m; sourceTree = ""; }; 50 | B3ADE16C1C1BD1D600AA4F63 /* libMLeaksFinder.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMLeaksFinder.a; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | B3ADE16F1C1BD1D600AA4F63 /* MLeaksFinder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MLeaksFinder.h; sourceTree = ""; }; 52 | B3ADE1781C1BD29600AA4F63 /* UIView+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+MemoryLeak.h"; sourceTree = ""; }; 53 | B3ADE1791C1BD29600AA4F63 /* UIView+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+MemoryLeak.m"; sourceTree = ""; }; 54 | B3ADE17B1C1BD2F000AA4F63 /* NSObject+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MemoryLeak.h"; sourceTree = ""; }; 55 | B3ADE17C1C1BD2F000AA4F63 /* NSObject+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MemoryLeak.m"; sourceTree = ""; }; 56 | B3ADE17E1C1BD4B200AA4F63 /* UIViewController+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+MemoryLeak.h"; sourceTree = ""; }; 57 | B3ADE17F1C1BD4B200AA4F63 /* UIViewController+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+MemoryLeak.m"; sourceTree = ""; }; 58 | B3ADE1811C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationController+MemoryLeak.h"; sourceTree = ""; }; 59 | B3ADE1821C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationController+MemoryLeak.m"; sourceTree = ""; }; 60 | B3ADE1841C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIPageViewController+MemoryLeak.h"; sourceTree = ""; }; 61 | B3ADE1851C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIPageViewController+MemoryLeak.m"; sourceTree = ""; }; 62 | B3ADE1871C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITabBarController+MemoryLeak.h"; sourceTree = ""; }; 63 | B3ADE1881C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITabBarController+MemoryLeak.m"; sourceTree = ""; }; 64 | B3ADE18A1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISplitViewController+MemoryLeak.h"; sourceTree = ""; }; 65 | B3ADE18B1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISplitViewController+MemoryLeak.m"; sourceTree = ""; }; 66 | B3C512C31CE3368B0032C2C8 /* UIApplication+MemoryLeak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIApplication+MemoryLeak.h"; sourceTree = ""; }; 67 | B3C512C41CE3368B0032C2C8 /* UIApplication+MemoryLeak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIApplication+MemoryLeak.m"; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | B3ADE1691C1BD1D600AA4F63 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | FE6CAD555020A33B697B5FDE /* libPods-MLeaksFinder.a in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | B3ADE1631C1BD1D600AA4F63 = { 83 | isa = PBXGroup; 84 | children = ( 85 | B3ADE16E1C1BD1D600AA4F63 /* MLeaksFinder */, 86 | B3ADE16D1C1BD1D600AA4F63 /* Products */, 87 | F2E3CE8BD1BB1CB88838AF63 /* Pods */, 88 | E3247A573B2F7B1C26211F01 /* Frameworks */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | B3ADE16D1C1BD1D600AA4F63 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | B3ADE16C1C1BD1D600AA4F63 /* libMLeaksFinder.a */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | B3ADE16E1C1BD1D600AA4F63 /* MLeaksFinder */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | B3667E4C1D38ADF6002B55DE /* MLeakedObjectProxy.h */, 104 | B3667E4D1D38ADF6002B55DE /* MLeakedObjectProxy.m */, 105 | B3ADE16F1C1BD1D600AA4F63 /* MLeaksFinder.h */, 106 | B3667E701D3BA78E002B55DE /* MLeaksMessenger.h */, 107 | B3667E711D3BA78E002B55DE /* MLeaksMessenger.m */, 108 | B3ADE17B1C1BD2F000AA4F63 /* NSObject+MemoryLeak.h */, 109 | B3ADE17C1C1BD2F000AA4F63 /* NSObject+MemoryLeak.m */, 110 | B3C512C31CE3368B0032C2C8 /* UIApplication+MemoryLeak.h */, 111 | B3C512C41CE3368B0032C2C8 /* UIApplication+MemoryLeak.m */, 112 | B3ADE1811C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.h */, 113 | B3ADE1821C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.m */, 114 | B3ADE1841C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.h */, 115 | B3ADE1851C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.m */, 116 | B3ADE18A1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.h */, 117 | B3ADE18B1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.m */, 118 | B3ADE1871C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.h */, 119 | B3ADE1881C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.m */, 120 | B3ADE1781C1BD29600AA4F63 /* UIView+MemoryLeak.h */, 121 | B3ADE1791C1BD29600AA4F63 /* UIView+MemoryLeak.m */, 122 | B3ADE17E1C1BD4B200AA4F63 /* UIViewController+MemoryLeak.h */, 123 | B3ADE17F1C1BD4B200AA4F63 /* UIViewController+MemoryLeak.m */, 124 | B32AAB301D767E59001485FF /* UITouch+MemoryLeak.h */, 125 | B32AAB311D767E59001485FF /* UITouch+MemoryLeak.m */, 126 | ); 127 | path = MLeaksFinder; 128 | sourceTree = ""; 129 | }; 130 | E3247A573B2F7B1C26211F01 /* Frameworks */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 6C69FD6BA6E3CCAC5168D790 /* libPods-MLeaksFinder.a */, 134 | ); 135 | name = Frameworks; 136 | sourceTree = ""; 137 | }; 138 | F2E3CE8BD1BB1CB88838AF63 /* Pods */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 90EF182415984ED6E908B442 /* Pods-MLeaksFinder.debug.xcconfig */, 142 | 5E297F00748C1FFA737EEFB0 /* Pods-MLeaksFinder.release.xcconfig */, 143 | ); 144 | name = Pods; 145 | sourceTree = ""; 146 | }; 147 | /* End PBXGroup section */ 148 | 149 | /* Begin PBXNativeTarget section */ 150 | B3ADE16B1C1BD1D600AA4F63 /* MLeaksFinder */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = B3ADE1751C1BD1D600AA4F63 /* Build configuration list for PBXNativeTarget "MLeaksFinder" */; 153 | buildPhases = ( 154 | 1E51A1E3538164E48103EBFB /* 📦 Check Pods Manifest.lock */, 155 | B3ADE1681C1BD1D600AA4F63 /* Sources */, 156 | B3ADE1691C1BD1D600AA4F63 /* Frameworks */, 157 | B3ADE16A1C1BD1D600AA4F63 /* CopyFiles */, 158 | C5C5444B869098CC15440C05 /* 📦 Copy Pods Resources */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | ); 164 | name = MLeaksFinder; 165 | productName = MLeaksFinder; 166 | productReference = B3ADE16C1C1BD1D600AA4F63 /* libMLeaksFinder.a */; 167 | productType = "com.apple.product-type.library.static"; 168 | }; 169 | /* End PBXNativeTarget section */ 170 | 171 | /* Begin PBXProject section */ 172 | B3ADE1641C1BD1D600AA4F63 /* Project object */ = { 173 | isa = PBXProject; 174 | attributes = { 175 | LastUpgradeCheck = 0710; 176 | ORGANIZATIONNAME = zeposhe; 177 | TargetAttributes = { 178 | B3ADE16B1C1BD1D600AA4F63 = { 179 | CreatedOnToolsVersion = 7.1.1; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = B3ADE1671C1BD1D600AA4F63 /* Build configuration list for PBXProject "MLeaksFinder" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | ); 190 | mainGroup = B3ADE1631C1BD1D600AA4F63; 191 | productRefGroup = B3ADE16D1C1BD1D600AA4F63 /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | B3ADE16B1C1BD1D600AA4F63 /* MLeaksFinder */, 196 | ); 197 | }; 198 | /* End PBXProject section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 1E51A1E3538164E48103EBFB /* 📦 Check Pods Manifest.lock */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "📦 Check Pods Manifest.lock"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 214 | showEnvVarsInLog = 0; 215 | }; 216 | C5C5444B869098CC15440C05 /* 📦 Copy Pods Resources */ = { 217 | isa = PBXShellScriptBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | inputPaths = ( 222 | ); 223 | name = "📦 Copy Pods Resources"; 224 | outputPaths = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MLeaksFinder/Pods-MLeaksFinder-resources.sh\"\n"; 229 | showEnvVarsInLog = 0; 230 | }; 231 | /* End PBXShellScriptBuildPhase section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | B3ADE1681C1BD1D600AA4F63 /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | B3C512C51CE3368B0032C2C8 /* UIApplication+MemoryLeak.m in Sources */, 239 | B3ADE17A1C1BD29600AA4F63 /* UIView+MemoryLeak.m in Sources */, 240 | B3ADE1861C1BD5CC00AA4F63 /* UIPageViewController+MemoryLeak.m in Sources */, 241 | B3667E4E1D38ADF6002B55DE /* MLeakedObjectProxy.m in Sources */, 242 | B3667E721D3BA78E002B55DE /* MLeaksMessenger.m in Sources */, 243 | B3ADE17D1C1BD2F000AA4F63 /* NSObject+MemoryLeak.m in Sources */, 244 | B3ADE18C1C1BD74700AA4F63 /* UISplitViewController+MemoryLeak.m in Sources */, 245 | B3ADE1801C1BD4B200AA4F63 /* UIViewController+MemoryLeak.m in Sources */, 246 | B3ADE1831C1BD54B00AA4F63 /* UINavigationController+MemoryLeak.m in Sources */, 247 | B32AAB321D767E59001485FF /* UITouch+MemoryLeak.m in Sources */, 248 | B3ADE1891C1BD6F000AA4F63 /* UITabBarController+MemoryLeak.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | B3ADE1731C1BD1D600AA4F63 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = dwarf; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | ENABLE_TESTABILITY = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 292 | MTL_ENABLE_DEBUG_INFO = YES; 293 | ONLY_ACTIVE_ARCH = YES; 294 | SDKROOT = iphoneos; 295 | }; 296 | name = Debug; 297 | }; 298 | B3ADE1741C1BD1D600AA4F63 /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 303 | CLANG_CXX_LIBRARY = "libc++"; 304 | CLANG_ENABLE_MODULES = YES; 305 | CLANG_ENABLE_OBJC_ARC = YES; 306 | CLANG_WARN_BOOL_CONVERSION = YES; 307 | CLANG_WARN_CONSTANT_CONVERSION = YES; 308 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INT_CONVERSION = YES; 312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 313 | CLANG_WARN_UNREACHABLE_CODE = YES; 314 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 315 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 316 | COPY_PHASE_STRIP = NO; 317 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 318 | ENABLE_NS_ASSERTIONS = NO; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | GCC_C_LANGUAGE_STANDARD = gnu99; 321 | GCC_NO_COMMON_BLOCKS = YES; 322 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 323 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 324 | GCC_WARN_UNDECLARED_SELECTOR = YES; 325 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 326 | GCC_WARN_UNUSED_FUNCTION = YES; 327 | GCC_WARN_UNUSED_VARIABLE = YES; 328 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 329 | MTL_ENABLE_DEBUG_INFO = NO; 330 | SDKROOT = iphoneos; 331 | VALIDATE_PRODUCT = YES; 332 | }; 333 | name = Release; 334 | }; 335 | B3ADE1761C1BD1D600AA4F63 /* Debug */ = { 336 | isa = XCBuildConfiguration; 337 | baseConfigurationReference = 90EF182415984ED6E908B442 /* Pods-MLeaksFinder.debug.xcconfig */; 338 | buildSettings = { 339 | OTHER_LDFLAGS = "-ObjC"; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | SKIP_INSTALL = YES; 342 | }; 343 | name = Debug; 344 | }; 345 | B3ADE1771C1BD1D600AA4F63 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | baseConfigurationReference = 5E297F00748C1FFA737EEFB0 /* Pods-MLeaksFinder.release.xcconfig */; 348 | buildSettings = { 349 | OTHER_LDFLAGS = "-ObjC"; 350 | PRODUCT_NAME = "$(TARGET_NAME)"; 351 | SKIP_INSTALL = YES; 352 | }; 353 | name = Release; 354 | }; 355 | /* End XCBuildConfiguration section */ 356 | 357 | /* Begin XCConfigurationList section */ 358 | B3ADE1671C1BD1D600AA4F63 /* Build configuration list for PBXProject "MLeaksFinder" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | B3ADE1731C1BD1D600AA4F63 /* Debug */, 362 | B3ADE1741C1BD1D600AA4F63 /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | B3ADE1751C1BD1D600AA4F63 /* Build configuration list for PBXNativeTarget "MLeaksFinder" */ = { 368 | isa = XCConfigurationList; 369 | buildConfigurations = ( 370 | B3ADE1761C1BD1D600AA4F63 /* Debug */, 371 | B3ADE1771C1BD1D600AA4F63 /* Release */, 372 | ); 373 | defaultConfigurationIsVisible = 0; 374 | defaultConfigurationName = Release; 375 | }; 376 | /* End XCConfigurationList section */ 377 | }; 378 | rootObject = B3ADE1641C1BD1D600AA4F63 /* Project object */; 379 | } 380 | --------------------------------------------------------------------------------