IOS 메모리 누출 검사 방법 및 MLeakFinder 다시 쓰기
11064 단어 IOS메모리 유출MLeakFinder
MLeakFinder - 원리
우선 UIVIew Controller를 보면 UIVIew Controller가 팝이나 디스미스에 걸렸을 때 이 VC는 이 VC에 포함된View나 하위 View가 빨리 방출됩니다.그래서 우리는 UIVIew Controller가 팝이나 디스미스에 의해 잠시 후에 이 VC에서의view,subView 등이 아직 존재하는지 확인해야 한다.
UIVIEW Controller + Memory Leak.h의load방법에서 볼 수 있듯이 아침+load방법에서runtime를 통해viewWill Appear,viewDid Appear,dismissViewController Animated:completion: 이 세 가지 방법을 교환했다.
1, 먼저 viewWill Appear 보기
- (void)swizzled_viewWillAppear:(BOOL)animated {
[self swizzled_viewWillAppear:animated];
objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN);
}
VC가 들어올 때 연관된 객체를 추가하고 NO로 표시합니다.2, viewDid Appear 보기
- (void)swizzled_viewDidDisappear:(BOOL)animated {
[self swizzled_viewDidDisappear:animated];
if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]) {
[self willDealloc];
}
}
코드를 통해 알 수 있듯이 현재 관련 대상의 태그를 가져와서 YES로 표시할 때 willDealloc를 호출합니다.3, 우리는 언제 YES로 표시될지 봅시다.
UINavigationController + MemoryLeak.h의 popViewController Animated: 방법에서 볼 수 있습니다.
- (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가 팝이나 왼쪽으로 미끄러져 돌아올 때 보기를 없애면 YES로 표시된다는 것을 알 수 있다.4, 우리는 윌딜록을 중점적으로 본다
- (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가 화이트리스트에 있는지 아닌지를 먼저 판단할 수 있습니다. 그렇다면 리턴 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, 3단계: 약한 바늘이 self를 가리키고 2s가 지연된 다음에 이 약한 바늘을 통해 - assert Not Dealloc를 호출합니다. 만약에 방출되면nil에게 메시지를 보내서 바로 되돌아옵니다. - assert Not Dealloc 방법을 터치하지 않고 방출되었다고 생각합니다.만약 그것이 방출되지 않았다면, - assert Not Dealloc이 호출될 것이다
__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 will Dealloc]
원본 좀 볼게요.
- (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, 첫 번째 단계: 상위 클래스의 willDealloc, 즉 위 디렉터리 4를 슈퍼를 통해 호출합니다.2, 2단계: willReleaseChildren, willReleaseChildren을 호출하여 이 대상의 하위 대상을 두루 훑어보고 방출 여부를 봅니다
- (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에서 현재 대상이 부모 노드 집합과 교차하는지 판단합니다.다음은 자세히 살펴보겠습니다. - assert Not Dealloc 방법.
- (void)assertNotDealloc { //
if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]) {
return;
} //
[MLeakedObjectProxy addLeakedObject:self];
NSString *className = NSStringFromClass([self class]);
NSLog(@"Possibly Memory Leak.
In case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.
View-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, 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에 프록시인 프록시를 연결합니다2단계:objc_를 통해setAssociatedObject(object,kLeakedObjectProxyKey,proxy,OBJC_ASSOCIATION_RETAIN) 방법,object는 proxy를 강하게 보유하고,proxy는 object를 보유하면,object가 방출하면proxy도 방출한다
3단계: proxy 저장.objectPtr (실제 개체 주소) 집합leakedObjectPtrs 안쪽으로
4단계: 탄창 AlertView약_INTERNAL_MLF_RC_ENABLED ==1, 탄창은 순환 참조를 감지하는 옵션을 추가합니다.만약_INTERNAL_MLF_RC_ENABLED = = 0, 스택 정보만 표시합니다.
LeakedObjectProxy 클래스의 경우 메모리 유출이 검출되어 발생한 것으로 유출 대상의 속성으로 존재하며, 유출된 대상이 방출되면 MLeakedObjectProxy도 방출되고 - dealloc 함수를 호출합니다
집합leakedObjectPtrs에서 이 대상의 주소를 제거하고 다시 창을 쳐서 이 대상이 방출되었음을 알립니다
6. 자신도 이 프레임워크를 다시 쓰려고 시도하고 있습니다. 여러분의 대화를 환영합니다.
이상은 IOS 메모리 유출 검사 방법 및 MLeakFinder의 상세한 내용을 다시 쓰는 것입니다. 더 많은 IOS 메모리 유출에 관한 자료는 저희 기타 관련 글을 주목해 주십시오!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
IOS에서 ReplayKit 및 RTC 사용 방법응용된 소리와 아나운서의 소리를 포함한다.이 두 가지 수요를 감안하여 우리는 스크린 공유를 하는 생방송에 필요한 미디어 흐름을 간단하게 분석할 수 있다. 만약 우리가 Audio App과 Audio Mic를 두 개의 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.