[英]Why does my view controller (presented modally from a storyboard segue) not get released after being dismissed?
My view controller (code below) is presented modally from a storyboard segue (attached to a UIButton). 我的视图控制器(下面的代码)是从故事板segue(附加到UIButton)以模态方式呈现的。 It is then dismissed once one of the (dynamically generated) buttons is tapped. 一旦(动态生成的)按钮被轻敲,它就会被解雇。 For some reason, it is not subsequently released (has a retain count of 1). 由于某种原因,它不会随后被释放(保留计数为1)。
Clearly the first concern would be the two objects (PPAPI and PPObjectCache) which hold this object as a delegate (registered in viewDidLoad), however these both use weak references which will automatically be NULLed by ARC if I can get whatever else is retaining it to release it. 显然,第一个问题是两个对象(PPAPI和PPObjectCache)将此对象保存为委托(在viewDidLoad中注册),但是这两个对象都使用弱引用,如果我可以获得保留它的任何其他内容,它将自动被ARC清空释放它。 I have verified that these objects are not holding a strong reference to this view controller. 我已经验证这些对象没有强烈引用此视图控制器。
I have used Instruments (the Allocations profile) to check the retains/releases of this object, and the report is shown below the code. 我使用了Instruments(Allocations配置文件)来检查此对象的保留/释放,并且报告显示在代码下方。 As you can see, the imbalance is in UIKit or Foundation code, but it's hard for me to see where exactly, or why. 正如您所看到的,不平衡在UIKit或Foundation代码中,但我很难看到究竟在哪里或为什么。
Can anyone spot the reason why UIKit or Foundation is holding on to my view controller? 任何人都能发现UIKit或者基金会持有我的视图控制器的原因吗?
#import "PPLoginViewController.h"
#import "PPAppDelegate.h"
@interface PPLoginViewController () {
NSArray *employeeIDs;
}
@end
@implementation PPLoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
PPAPI *api = [PPAPI api];
[api subscribeToViewsOfType:@"employees" delegate:self];
[[PPObjectCache cache] subscribeToChangesToObjectsOfType:@"worker" delegate:self];
[api callMethod:@"main.tasks.get_employees" withParameters:@{} callback:nil];
}
// PPAPI view subscription delegate method
- (void)receivedObjectIDs:(NSArray *)objectIDs forViewType:(NSString *)viewType {
NSLog(@"%@: GOT VIEW", self);
dispatch_async(dispatch_get_main_queue(), ^{
employeeIDs = objectIDs;
[self reload];
});
}
// PPObjectCache delegate method
- (void)objectChanged:(id)object {
dispatch_async(dispatch_get_main_queue(), ^{
[self reload];
});
}
- (void)reload {
[[self.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:[UIButton class]];
}]] makeObjectsPerformSelector:@selector(removeFromSuperview)];
UIDevice *currentDevice = [UIDevice currentDevice];
CGFloat y = 75.f;
for (NSNumber *employeeID in employeeIDs) {
NSDictionary *employee = [[PPObjectCache cache] objectOfType:@"worker" withID:employeeID];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:employee[@"name"] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont boldSystemFontOfSize:18.f];
button.tag = [employeeID integerValue];
button.frame = CGRectMake((self.view.bounds.size.width - 240.f) / 2.f, y, 240.f, currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad ? 60.f : 45.f);
[button addTarget:self action:@selector(login:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
y += currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad ? 75.f : 65.f;
}
}
// UIButton action from above
- (void)login:(UIButton *)sender {
PPAppDelegate *delegate = (PPAppDelegate *)[UIApplication sharedApplication].delegate;
delegate.loggedInEmployee = [[PPObjectCache cache] objectOfType:@"worker" withID:@(sender.tag)];
// self.logoutButton is an @property (weak) set from the presenting view controller
self.logoutButton.title = [NSString stringWithFormat:@"Logout %@", delegate.loggedInEmployee[@"initials"]];
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
Allocations report follows (sorry for poor formatting). 随后是分配报告(抱歉格式不佳)。 As you can see, the events for which my application code is responsible are all balanced (show as Retain/Release groups or are immediately followed by balancing calls). 如您所见,我的应用程序代码负责的事件都是平衡的(显示为保留/释放组或紧接着平衡调用)。 Something in UIKit or Foundation isn't releasing my view controller! UIKit或Foundation中的东西没有发布我的视图控制器!
# Event Type ∆ RefCt RefCt Timestamp Responsible Library Responsible Caller
0 Malloc +1 1 00:19.062.502 UIKit -[UIClassSwapper initWithCoder:]
1 Retain +1 2 00:19.062.991 UIKit -[UIRuntimeConnection initWithCoder:]
2 Retain +1 3 00:19.063.036 UIKit -[UIRuntimeConnection initWithCoder:]
3 Retain +1 4 00:19.063.230 UIKit UINibDecoderDecodeObjectForValue
4 Retain +1 5 00:19.063.274 UIKit UINibDecoderDecodeObjectForValue
5 Retain +1 6 00:19.063.315 Foundation -[NSObject(NSKeyValueCoding) setValue:forKey:]
6 Retain +1 7 00:19.063.388 UIKit -[UINib instantiateWithOwner:options:]
Release (2) -2 00:19.063.568 UIKit -[UINibDecoder finishDecoding]
8 Release -1 5 00:19.063.599 UIKit -[UINibDecoder finishDecoding]
10 Release -1 3 00:19.063.722 UIKit -[UIRuntimeConnection dealloc]
11 Release -1 2 00:19.063.755 UIKit -[UIRuntimeConnection dealloc]
12 Retain +1 3 00:19.063.920 UIKit -[UIStoryboardSegue initWithIdentifier:source:destination:]
Retain/Release (2) 00:19.067.387 Purchase -[PPMasterViewController prepareForSegue:sender:]
15 Retain +1 4 00:19.072.460 UIKit -[UIViewController setChildModalViewController:]
16 Retain +1 5 00:19.076.305 UIKit -[UINib instantiateWithOwner:options:]
17 Retain +1 6 00:19.076.366 UIKit -[UINib instantiateWithOwner:options:]
18 Retain +1 7 00:19.076.582 UIKit -[UIProxyObject initWithCoder:]
19 Retain +1 8 00:19.076.587 UIKit -[UIRuntimeConnection initWithCoder:]
20 Retain +1 9 00:19.080.167 UIKit UINibDecoderDecodeObjectForValue
21 Retain +1 10 00:19.080.230 UIKit UINibDecoderDecodeObjectForValue
22 Release -1 9 00:19.080.449 UIKit -[UINib instantiateWithOwner:options:]
23 Release -1 8 00:19.080.507 UIKit -[UINib instantiateWithOwner:options:]
24 Release -1 7 00:19.080.578 UIKit -[UINibDecoder finishDecoding]
Release (2) -2 00:19.080.602 UIKit -[UINibDecoder finishDecoding]
26 Release -1 5 00:19.080.716 UIKit -[UIRuntimeConnection dealloc]
Retain/Release (2) 00:19.081.788 Purchase -[PPAPI subscribeToViewsOfType:delegate:]
Retain/Release (2) 00:19.081.866 Purchase -[PPObjectCache subscribeToChangesToObjectsOfType:delegate:]
32 Retain +1 5 00:19.089.975 UIKit -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:]
33 Retain +1 6 00:19.091.352 UIKit __91-[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]_block_invoke_0238
Retain/Release (2) 00:19.105.379 UIKit -[UIResponder becomeFirstResponder]
36 Retain +1 7 00:19.106.122 UIKit -[UIViewController presentViewController:withTransition:completion:]
37 Retain +1 8 00:19.106.142 UIKit -[UIViewController presentViewController:withTransition:completion:]
38 Release -1 7 00:19.108.717 UIKit _UIApplicationHandleEvent
39 Release -1 6 00:19.109.517 UIKit -[UIStoryboardSegue dealloc]
40 Release -1 5 00:19.109.534 UIKit _UIApplicationHandleEvent
41 Release -1 4 00:19.109.581 UIKit -[UIStoryboardScene dealloc]
Retain/Autorelease/Release (5) +1 00:19.145.293 Foundation -[NSConcreteHashTable countByEnumeratingWithState:objects:count:]
Retain (2) +2 00:19.151.847 Purchase -[PPLoginViewController receivedObjectIDs:forViewType:]
Retain (2) +2 00:19.151.874 Purchase __copy_helper_block_
Release (2) -2 00:19.151.888 Purchase -[PPLoginViewController receivedObjectIDs:forViewType:]
Release (2) -2 00:19.278.813 Purchase __destroy_helper_block_
48 Release -1 4 00:19.541.189 UIKit -[UIWindowController transitionViewDidComplete:fromView:toView:removeFromView:]
Retain/Release (2) 00:19.996.260 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:]
51 Release -1 3 00:19.996.269 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:]
52 Release -1 2 00:19.996.302 UIKit -[UIPeripheralHost(UIKitInternal) _stopPinningInputViewsOnBehalfOfResponder:]
53 Retain +1 3 00:19.996.776 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:]
54 Retain +1 4 00:20.001.379 UIKit __91-[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]_block_invoke_0238
55 Release -1 3 00:20.002.196 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:]
56 Retain +1 4 00:20.432.653 UIKit -[UIViewController _didFinishDismissTransition]
57 Release -1 3 00:20.432.658 UIKit -[UIViewController setChildModalViewController:]
58 Release -1 2 00:20.432.662 UIKit -[UIViewController _didFinishDismissTransition]
59 Release -1 1 00:20.432.706 UIKit -[UIWindowController transitionViewDidComplete:fromView:toView:removeFromView:]
Retain/Autorelease (2) +1 00:20.663.794 Foundation hashProbe
This was my own stupid fault, as I expected, but I'll answer it here in case it's useful to anyone! 这是我自己的愚蠢错误,正如我所料,但我会在这里回答,以防它对任何人都有用!
PPAPI
maintains a NSHashTable
with weak references for the view subscriptions, which will be automatically zeroed by ARC if the subscribed object is released. PPAPI
维护一个NSHashTable
,其中包含对视图订阅的弱引用,如果订阅的对象被释放,它将自动归零。 It appears that when getting objects from NSHashTable
, they are retained and then autoreleased by Foundation. 看来,当从NSHashTable
获取对象时,它们会被保留,然后由Foundation自动释放。
PPAPI was accessing the NSHashTable within a while(1)
loop on a background GCD queue (this is probably bad design). PPAPI在后台GCD队列上的while(1)
循环内访问NSHashTable(这可能是糟糕的设计)。 Because the while(1)
loop kept the queue busy, the autorelease pool was never drained, with the result that PPLoginViewController
was retained by Foundation and never released. 因为while(1)
循环使队列保持忙碌,所以自动释放池从未耗尽,结果是PPLoginViewController
被Foundation保留并且从未被释放。
A workaround was putting the content of the while(1)
loop in an @autoreleasepool { }
block. 解决方法是将while(1)
循环的内容放在@autoreleasepool { }
块中。
The clue to this was the last line of the Allocations report, which shows Retain/Autorelease
rather than Retain/Autorelease/Release
. 对此的线索是Allocations报告的最后一行,该报告显示了Retain/Autorelease
而不是Retain/Autorelease/Release
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.