更改viewControllers属性后的UITabBarController异常

时间:2012-08-18 20:37:47

标签: ios cocoa-touch uiviewcontroller uitabbarcontroller

我有一个带有两个标签的UITabBarController。 TAB A是一个UIViewController,TAB B是一个从笔尖加载的UIViewController。

我试图让我在从TAB B移动到TAB A或从TAB B移动任何其他选项卡时,我想将TAB B重置为其初始状态。我这样做只是创建一个新的并在viewControllers数组中替换它。问题是,在我重置UIViewController之后,我得到一条错误,就是“发送到解除分配的实例的消息。这通常是这两个错误之一:

*** -[AddCompetitionViewController isEqual:]: message sent to deallocated instance...
*** -[AddCompetitionViewController retain]: message sent to deallocated instance...

这是在我从TAB B切换回TAB A后立即发生的。

这是我用来替换标签的代码,我尝试隔离问题。

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
    NSLog(@"Should Select: %@", viewController);
    return YES;
}

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
    NSLog(@"Did Select: %@", viewController);
    if ([viewController class] != [AddCompetitionViewController class]) {
        AddCompetitionViewController *ACViewController = [[AddCompetitionViewController alloc] initWithNibName:@"AddCompetition" bundle:[NSBundle mainBundle] andDancer:self.currentDancer];
        UITabBarItem *ACitem = [[UITabBarItem alloc] initWithTitle:@"Add Comp." image:[UIImage imageNamed:@"Addcomp.png"] tag:0];
        ACViewController.tabBarItem = ACitem;

        NSMutableArray *arr = [[NSMutableArray alloc] initWithArray:self.viewControllers];
        [arr replaceObjectAtIndex:1 withObject:ACViewController];
        [self setViewControllers:arr];
        NSLog(@"Replaced: %@", [arr objectAtIndex:1]);
    }
}

(我正在使用ARC)

使用NSLogs,我已经能够确定当我尝试切换回TAB A时,应用程序正在尝试引用旧的原始UIViewController,而不是新的UIViewController。 tabBarController:didSelectViewController:是最后一个被调用的东西,我没有任何代码在替换后引用UIViewController的代码。我也尝试添加一个异常断点,但是它只列出了一堆十六进制数据,然后默认为application main。这是我的日志:

2012-08-18 16:20:05.479 How's My Feisin'[4780:c07] Should Select: <AddCompetitionViewController: 0x78a5af0>
2012-08-18 16:20:05.489 How's My Feisin'[4780:c07] Did Select: <AddCompetitionViewController: 0x78a5af0>
2012-08-18 16:20:06.885 How's My Feisin'[4780:c07] Should Select: <CompetitionListViewController: 0x78a7550>
2012-08-18 16:20:06.887 How's My Feisin'[4780:c07] Did Select: <CompetitionListViewController: 0x78a7550>
2012-08-18 16:20:06.887 How's My Feisin'[4780:c07] Replaced: <AddCompetitionViewController: 0x6c5d0f0>
2012-08-18 16:20:09.290 How's My Feisin'[4780:c07] *** -[AddCompetitionViewController retain]: message sent to deallocated instance 0x78a5af0

回溯:

* thread #1: tid = 0x1c03, 0x017cddee CoreFoundation`___forwarding___ + 206, stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x017cddee CoreFoundation`___forwarding___ + 206
frame #1: 0x017cdcb2 CoreFoundation`_CF_forwarding_prep_0 + 50
frame #2: 0x0176e2c0 CoreFoundation`CFRetain + 96
frame #3: 0x01798ab9 CoreFoundation`CFArrayApplyFunction + 57
frame #4: 0x0072ffbb UIKit`_afterCACommitHandler + 255
frame #5: 0x0183b99e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
frame #6: 0x017d2640 CoreFoundation`__CFRunLoopDoObservers + 384
frame #7: 0x0179e4c6 CoreFoundation`__CFRunLoopRun + 1174
frame #8: 0x0179dd84 CoreFoundation`CFRunLoopRunSpecific + 212
frame #9: 0x0179dc9b CoreFoundation`CFRunLoopRunInMode + 123
frame #10: 0x01c447d8 GraphicsServices`GSEventRunModal + 190
frame #11: 0x01c4488a GraphicsServices`GSEventRun + 103
frame #12: 0x0071f626 UIKit`UIApplicationMain + 1163
frame #13: 0x00002595 How's My Feisin'`main + 181 at main.m:16
frame #14: 0x000024d5 How's My Feisin'`start + 53

任何帮助搞清楚这个错误,或找到一个更好的地方来替换UIViewController都会很棒! 谢谢!

编辑:我找到了导致问题的原因,但仍无法解决问题。即使我从未见过UITabBarController为视图更改设置动画,但它显然仍然会预制动画。我设法分析了应用程序,_afterCACommitHandler正在对象上执行额外的发布。

编辑:我已经找到了解决方法,我在tabBarController和TAB B的UIViewController之间放了另一个UIViewController。然后让新的UIViewController重置旧的。它有效,但这不是正确的做事方式。我仍然不知道造成额外释放的原因。

2 个答案:

答案 0 :(得分:0)

我认为问题在于您创建了一个新数组arr,您可以在其中替换一个视图控制器,但您永远不会将标签栏控制器的viewControllers数组重置为该数组。在[arr replaceObjectAtIndex:1]语句之后,尝试添加以下行:

tabBarController.viewControllers = arr;

答案 1 :(得分:0)

我创建了一个解决方法,在另一个UIViewController中封装了TAB B的UIViewController。这个中间控制器是UITabBarController的委托,并释放并重新创建我想在选项卡更改时重置的UIViewController。这可以防止标签栏控制器中缺少ViewController引用。

这是中间阶层:

.h文件

#import <UIKit/UIKit.h>
#import "AddCompetitionViewController.h"
#import "Dancer.h"

@interface AddCompResetViewController : UIViewController <UITabBarControllerDelegate>
{
    BOOL willReset;
}

@property (nonatomic, retain) Dancer *currentDancer;
@property (nonatomic, retain) AddCompetitionViewController *ACViewController;

- (id)initWithDancer:(Dancer *)dancer andTabBarController:(UITabBarController *)aController;
@end

.m文件

@implementation AddCompResetViewController
@synthesize currentDancer, ACViewController;

- (id)initWithDancer:(Dancer *)dancer andTabBarController:(UITabBarController* )aController;
{
    if ((self = [super init])) {
        self.currentDancer = dancer;
        //create first instance
        self.ACViewController = [[AddCompetitionViewController alloc] initWithNibName:@"AddCompetition" bundle:[NSBundle mainBundle] andDancer:self.currentDancer];
        self.ACViewController.theTabBarController = aController;
        [self.view addSubview:self.ACViewController.view];
        willReset = NO;
    }
    return self;
}

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
    //reset
    if (viewController != self) {
        AddCompetitionViewController *AViewController = [[AddCompetitionViewController alloc] initWithNibName:@"AddCompetition" bundle:[NSBundle mainBundle] andDancer:self.currentDancer];
        [self.ACViewController.view removeFromSuperview];
        self.ACViewController = nil;
        self.ACViewController = AViewController;
        [self.view addSubview:self.ACViewController.view];
        willReset = NO;
    } else if (viewController == self) {
        willReset = YES;
    }
}

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
    if (viewController.tabBarItem.tag == 4) {
        [self.navigationController popToRootViewControllerAnimated:YES];
        return NO;
    } else {
        return YES;
    }
}