父/子导航UIViewController导致编辑表单异常



我在应用程序中有一个父->子导航设置。我使用导航通过pushViewController函数。

-(void)loadMemosViewController:(id)sender{
    if(activeHullGuid != nil && activeHullGuid.length > 0)
    {
        NSString *storyboardName = @"MainStoryboard_iPhone1";

        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
        MemosViewController *loginVC = [storyboard instantiateViewControllerWithIdentifier:@"sid_Memos"];
        loginVC.keyReference = [[KeyValuePairIS alloc] initWithData:&controllerID:activeHullGuid];

        [self.navigationController pushViewController:loginVC animated:YES];
    }
}
对于后退导航,我只使用IOS中的默认实现(即点击后退按钮)。此设置适用于大多数情况,但最近的实现会产生问题。

问题是:
我有父视图控制器名为"hullViewController"和一个子"memosViewController"。它们之间的导航是有效的。孩子没有向父母报告任何信息。HullViewController也是一个可编辑的表单,它通过导航栏中的按钮改变编辑状态。
现在,如果我改变这个编辑/读取状态在hullViewController不间断地工作。如果我访问子memosViewController,并回到父,我只能再次改变状态,然后应用程序崩溃与exc_bad_access code=1。在与"僵尸"分析后,我发现例外的罪魁祸首是我可能处置的子memosViewController。
一个Objective-C消息被发送到一个被释放的'MemosViewController'对象(僵尸)在地址:0xdd52f10

似乎会在IOS内部事件中崩溃,因为在崩溃之前我的断点都没有被击中。

A你可以看到子节点在创建过程中被实例化了,我没有引用它。为什么编辑状态更改请求子对象?

我已经试过了:
将MemosViewController声明为一个类变量。(应用程序不再崩溃,但不再改变状态)。
-在viewDidLoad上初始化MemosViewController,没有改变任何东西。
-只调用子类init(不通过storyboard),加载没有UI的子,但结果是一样的。

项目是用ARC设置的,所以我对对象的处理有最小的控制。

我一直在寻找一个解决方案,很长一段时间,没有结果。如果我去看望孩子,任何帮助解决我编辑错误的人都将不胜感激。

我还发现,当我从child返回到parent时,引用self。navigationItem仍然指向child,任何对导航按钮的更新都会导致应用程序崩溃。

**附加自定义ViewController,因为它可能与问题**有关

#import "UITableViewControllerEx.h"
#import "UITextFieldEx.h"
#import "UITextViewEx.h"
#import "GlobalValues.h"
#import "UITableViewEx.h"

@interface UITableViewControllerEx ()
@end
@implementation UITableViewControllerEx
UIBarButtonItem *bbi_navigateToMaster;
UIBarButtonItem *editButton;
UIButton *cmdEdit;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    [self setNavigationBackground];

    [self setApplicationTintColor];
    [self setApplicationTitleFont];
    [self setupLeftBarButtonItem];

    [self setBackButton];
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
//UITextFieldEx delegate to control the length of fields
- (BOOL)textField:(UITextFieldEx *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    NSUInteger newLength = [textField.text length] + [string length] - range.length;
    return (newLength > textField.maxLength) ? NO : YES;
}
//UITextViewEx delegate to control the length of fields
-(BOOL)textView:(UITextViewEx *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    NSUInteger newLength = [textView.text length] + [text length] - range.length;
    return (newLength > textView.maxLength) ? NO : YES;
}
//function to set left button to always pop to root controller
- (void)setBackButtonToReturnToMaster {
    UIButton *cmdHome = [[UIButton alloc] initWithFrame:CGRectMake(0,0,30,30)];
    [cmdHome setImage:[UIImage imageNamed:@"home"] forState:UIControlStateNormal];
    bbi_navigateToMaster = [[UIBarButtonItem alloc] initWithCustomView:cmdHome];
    [cmdHome addTarget:self action:@selector(backToMaster:) forControlEvents:UIControlEventTouchUpInside ];
    self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:bbi_navigateToMaster , nil];
    /*
     bbi_navigateToMaster = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:self action:@selector(backToMaster:)];
     self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:bbi_navigateToMaster , nil];
     [bbi_navigateToMaster setImage:[UIImage imageNamed:@"home"]];
     [bbi_navigateToMaster setImageInsets:UIEdgeInsetsMake(2, 2, 2, 2)];*/
}
//pop to root controller
-(void)backToMaster:(id)sender {
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    }
    else { [self.navigationController popToRootViewControllerAnimated:YES]; }

}

//find superview element of given type
- (UIView *)findSuperViewWithClass:(Class)superViewClass uiViewToSearch:(UIView*)bottomView{
    UIView *superView = bottomView.superview;
    UIView *foundSuperView = nil;
    while (nil != superView && nil == foundSuperView) {
        if ([superView isKindOfClass:superViewClass]) {
            foundSuperView = superView;
            break;
        } else {
            superView = superView.superview;
        }
    }
    return foundSuperView;
}
-(void)setNavigationBackground{
    if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)] ) {
        UIImage *image = [UIImage imageNamed:@"navigationBackground"];
        [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
        UIView* uv = [[UIView alloc] initWithFrame:CGRectMake(0, self.navigationController.navigationBar.frame.size.height-1,self.navigationController.navigationBar.frame.size.width, 1)];
        [uv setBackgroundColor:[GlobalValues getTintColor]];
        [self.navigationController.navigationBar insertSubview:uv atIndex:10];
    }
}
//sets the tint color of szstem items (title, szstem buttons, ...)
-(void)setApplicationTintColor {
    NSArray *ver = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."];
    if ([[ver objectAtIndex:0] intValue] >= 7) {
        self.navigationController.navigationBar.barTintColor = [GlobalValues getTintColor];
        self.navigationController.navigationBar.tintColor = [GlobalValues getTintColor];
        self.navigationController.navigationBar.translucent = NO;
        [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];
        UIColor *color = [GlobalValues getTintColor];
        self.view.tintColor = color;
    }else {
        //self.navigationController.navigationBar.tintColor = [GlobalValues getTintColor];
        /*NSDictionary *textTitleOptions = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], UITextAttributeTextColor, [UIColor clearColor], UITextAttributeTextShadowColor, nil];
         [[UINavigationBar appearance] setTitleTextAttributes:textTitleOptions];*/
    }
}
//sets the navigation title
-(void)setApplicationTitleFont {
    NSArray *ver = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."];
    if ([[ver objectAtIndex:0] intValue] >= 7) {
        [self.navigationController.navigationBar setTitleTextAttributes:
         [NSDictionary dictionaryWithObjectsAndKeys:
          [UIFont fontWithName:@"HelveticaNeue-Light" size:21],
          NSFontAttributeName, [UIColor whiteColor], UITextAttributeTextColor, [UIColor clearColor], UITextAttributeTextShadowColor, nil]];
    }else {
        [self.navigationController.navigationBar setTitleTextAttributes: @{
                                                                           UITextAttributeTextColor: [UIColor whiteColor],
                                                                           UITextAttributeFont: [UIFont fontWithName:@"Helvetica-Light" size:21.0f]
                                                                           }];
    }
}
-(void)setupLeftBarButtonItem{
    cmdEdit = [[UIButton alloc] initWithFrame:CGRectMake(0,0,30,30)];
    [cmdEdit setImage:[UIImage imageNamed:@"locked"]  forState:UIControlStateNormal];
    editButton = [[UIBarButtonItem alloc] initWithCustomView:cmdEdit];
    [cmdEdit addTarget:self action:@selector(setEditState) forControlEvents:UIControlEventTouchUpInside];

}
- (UIBarButtonItem *)leftBarButtonItem
{
    if (self.tableView.editing) {
        [cmdEdit setImage:[UIImage imageNamed:@"unlocked"]  forState:UIControlStateNormal];
        return editButton;
    }
    else {
        [cmdEdit setImage:[UIImage imageNamed:@"locked"]  forState:UIControlStateNormal];
        return editButton;
    }

}
-(void)updateEditButton{
    if (self.tableView.editing) {
        [cmdEdit setImage:[UIImage imageNamed:@"unlocked"]  forState:UIControlStateNormal];
    }
    else {
        [cmdEdit setImage:[UIImage imageNamed:@"locked"]  forState:UIControlStateNormal];
    }
}
-(void)setEditState{
    if (!self.tableView.editing) {
        [self setEditing:YES animated:YES];
    } else {
        [self setEditing:NO animated:YES];
    }
    [self updateEditButton];
}

 }*/
-(void) setBackButton{
    UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    UIImage *backBtnImage = [UIImage imageNamed:@"back"]  ;
    [backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
    [backBtn addTarget:self action:@selector(goback) forControlEvents:UIControlEventTouchUpInside];
    backBtn.frame = CGRectMake(0, 0, 30, 30);
    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:backBtn] ;
    self.navigationItem.leftBarButtonItem = backButton;
}
- (void)goback
{
    [self.navigationController popViewControllerAnimated:YES];
}
#pragma mark - Table view data source

#pragma mark - Table view delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundView=[[UIView alloc] initWithFrame:CGRectZero];
    cell.backgroundColor = [UIColor clearColor];
    cell.layer.backgroundColor = [UIColor clearColor].CGColor;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *customTitleView = [ [UIView alloc] initWithFrame:CGRectMake(10, 0, 300, 44)];
    UIView *customTitleLineView = [ [UIView alloc] initWithFrame:CGRectMake(10, 43, self.view.frame.size.width -20, 0.5f)];
    customTitleLineView.backgroundColor = [GlobalValues getTintColor];

    UILabel *titleLabel = [ [UILabel alloc] initWithFrame:CGRectMake(20, 0, 300, 44)];
    titleLabel.text = [self tableView:tableView titleForHeaderInSection:section];
    titleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:18];
    titleLabel.textColor = [GlobalValues getTintColor];
    titleLabel.backgroundColor = [UIColor clearColor];
    if (titleLabel.text.length != 0) {
        [customTitleView addSubview:customTitleLineView];
    }
    [customTitleView addSubview:titleLabel];
    return customTitleView;
}
@end

看来我找到解决问题的办法了。

UITableViewControllerEx类包含设置编辑按钮的功能。类变量"UIBarButtonItem *editButton;"然后在所有继承自"UITableViewControllerEx"的表单上用作编辑按钮

解决方案是在继承UITableViewControllerEx与本地名称(如editButtonHull)的每个表单上实例化UIBarButtonItem,并作为参数给予超类的逻辑。

感谢@akashg的建议,导航栏的修改可能是问题所在

最新更新