简体   繁体   English

为什么我的视图控制器(从故事板中以模态方式呈现)在被解雇后不会被释放?

[英]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.

相关问题 模态显示/关闭视图控制器时通知? - Notification when view controller is presented modally/dismissed? willRotateToInterfaceOrientation未在模态呈现的视图控制器中调用 - willRotateToInterfaceOrientation not being called in a modally presented view controller 导航 Controller segue 模态呈现 - Navigation Controller segue is presented modally 如何在以模态显示的视图控制器上执行当前模态搜索? - How can I perform a present modally segue on a view controller that is presented modally? 在发送方视图控制器被解除后执行 segue - Performing segue after sender view controller is dismissed 从模态呈现的视图控制器导航到根视图控制器 - Navigate to root view-controller from modally presented view controller 关闭从模态呈现的视图控制器翻转的视图控制器 - Dismiss view controller flipped from modally presented view controller 为什么在一次调用present(_:animated:completion :)方法后,我的ViewController连续两次模态显示? - Why does my ViewController get modally presented two times in a row after a single call to present(_:animated:completion:) method? 旋转模态呈现的子视图控制器时,为什么我的约束不起作用? - Why are my constraints not working when rotating a modally presented child view controller? 为什么视图控制器在 Button Segue 上的 Storyboard 中移动 - Why does View Controller Shift in Storyboard on Button Segue
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM