1

我收到以下错误。

EXC_BAD_ACCESS  
KERN_INVALID_ADDRESS 
[UITextField keyboardInputChangedSelection:]

直到这份最新的崩溃报告,我一直无法重现这次崩溃。

我根据我的应用程序中是否设置了didFinishLaunchingWithOptions密码/密码调用此视图控制器。applicationWillEnterForeground

现在这是奇怪的事情。

崩溃发生在以下场景中。

  1. 我在没有设置引脚的情况下运行我的应用程序,我设置了引脚。
  2. 然后按主页按钮。
  3. 这个视图控制器弹出,我输入 pin 并且视图被关闭。
  4. 然后我按下主页按钮。
  5. 我进入应用程序,出现固定屏幕。
  6. 我按回家。
  7. 然后进入应用程序,我得到一个崩溃。

我正在使用 arc,我在 iOS6 上重现了崩溃。

下面是我的代码,我只是看不到问题吗?

.h 文件

#import <UIKit/UIKit.h>

#define PVSectionFooterDefault @"Enter your PIN"
#define PVSectionFooterInvalid @"Invalid!"
#define PVSectionFooterCorrect @"Correct!"

@interface PasscodeViewController : UIViewController <UITableViewDelegate, 
       UITableViewDataSource, UITextFieldDelegate>{
    IBOutlet UITableView *passcodeTable;
    IBOutlet UILabel *lblMessage;
}
@property (nonatomic, strong) UITableView *passcodeTable;
@property (nonatomic, strong) UITextField   *txtPassword;
@property (nonatomic, strong) UILabel *lblMessage;
@end

.m 文件

#import "PasscodeViewController.h"
#import "AppDelegate.h"

@implementation PasscodeViewController
@synthesize passcodeTable;
@synthesize txtPassword;
@synthesize lblMessage;
- (void)viewDidLoad {
    [super viewDidLoad];

    //[passcodeTable setBackgroundColor:[UIColor clearColor]];
    //[passcodeTable setBackgroundView:nil];
    self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];

    lblMessage.shadowColor       = [UIColor whiteColor];
    lblMessage.shadowOffset      = CGSizeMake(0.0, 1.0);
    lblMessage.font              = [UIFont systemFontOfSize:14];
    lblMessage.textColor         = [UIColor darkGrayColor];
    lblMessage.text = PVSectionFooterDefault;

    txtPassword.isAccessibilityElement = YES;
    txtPassword.accessibilityLabel = @"Enter PIN";
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self
         selector:@selector(willShowKeyboard:) 
             name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
         selector:@selector(didShowKeyboard:) 
             name:UIKeyboardDidShowNotification object:nil];

}

- (void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self 
             name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self 
             name:UIKeyboardDidShowNotification object:nil];
}

- (void)willShowKeyboard:(NSNotification *)notification {
    [UIView setAnimationsEnabled:NO];
}

- (void)didShowKeyboard:(NSNotification *)notification {
    [UIView setAnimationsEnabled:YES];
}

#pragma mark - Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView 
              numberOfRowsInSection:(NSInteger)section {
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView 
                    cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView 
                    dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] 
                    initWithStyle:UITableViewCellStyleDefault 
                  reuseIdentifier:CellIdentifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        CGRect frame = CGRectMake(cell.frame.origin.x+205, 
                    cell.frame.origin.y+7, 90, 31);

        txtPassword = [[UITextField alloc] initWithFrame:frame];
        txtPassword.delegate = self;
        txtPassword.borderStyle = UITextBorderStyleRoundedRect;
        txtPassword.font = [UIFont systemFontOfSize:17.0];
        txtPassword.backgroundColor = [UIColor whiteColor];
        txtPassword.contentVerticalAlignment = 
                     UIControlContentVerticalAlignmentCenter;
        txtPassword.keyboardType = UIKeyboardTypeNumberPad;
        txtPassword.returnKeyType = UIReturnKeyDone;    
        txtPassword.secureTextEntry = YES;
        txtPassword.clearButtonMode = UITextFieldViewModeAlways;    

        cell.textLabel.text = @"Enter PIN";

        [cell addSubview:txtPassword];

        [txtPassword becomeFirstResponder];     
    }
    return cell;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    return YES;
}

- (BOOL) textFieldShouldClear:(UITextField *)textField {
    lblMessage.text = PVSectionFooterDefault;   
    return YES;
}

- (BOOL)textField:(UITextField *)textField 
           shouldChangeCharactersInRange:(NSRange)range 
                       replacementString:(NSString *)string {
    BOOL res = TRUE;
    NSString *newString = [textField.text 
             stringByReplacingCharactersInRange:range 
                                     withString:string];

    if ([newString length] == 4) {
        NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
        NSString *strPassword = [myDefaults stringForKey:@"pass"];

        if (strPassword == nil) {
            strPassword = @"";
        }

        if (![newString isEqualToString:strPassword]) {
            lblMessage.text = PVSectionFooterInvalid;
        } else {
            lblMessage.text = PVSectionFooterCorrect;

            AppDelegate *appDel = (AppDelegate*) 
                        [[UIApplication sharedApplication] delegate];
            appDel.gbooShowingGetStartedPasswordAsk = FALSE;
            [self.view removeFromSuperview];
        }
    } else if ([newString length] < 4) {
        lblMessage.text = PVSectionFooterDefault;
    }

    res = !([newString length] > 4);

    return res;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
    [super viewDidUnload];
}
@end

应用程序委托确实启动了

if (![strPassword isEqualToString:@""]) {
        self.gbooShowingGetStartedPasswordAsk = TRUE;

        lvc = [[PasscodeViewController alloc] 
               initWithNibName:@"PasscodeView" bundle:nil];

        lvc.view.frame = CGRectMake(0, 20, 320, 460); 

        [window addSubview:lvc.view];
    }

和....

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];

    if (self.gbooShowingGetStartedPasswordAsk == FALSE) {

        NSString *strPassword = [myDefaults stringForKey:@"pass"];
        if (strPassword == nil) {
            strPassword = @"";
        }

        if (![strPassword isEqualToString:@""]) {
            lvc = [[PasscodeViewController alloc] 
                   initWithNibName:@"PasscodeView" bundle:nil];
            int th = self.window.frame.size.height;
            lvc.view.frame = CGRectMake(0, 20, 320, th); 

            lvc.view.backgroundColor = [UIColor groupTableViewBackgroundColor];

            [window addSubview:lvc.view];
        }
    }
}

在此处输入图像描述

4

2 回答 2

3

考虑什么时候lvcis not nil,并且有一个超级视图,并且applicationWillEnterForeground:被调用,并将分支带到那里的主代码块中。您分配lvc给 a 的新实例PasscodeViewController,这可能会导致 的旧值lvc被释放。但是,它的view属性有一个超级视图,因此不会与它一起被释放。这些活着的对象可能会产生通知或动作,释放的视图控制器仍在监听。我还注意到,您并不总是取消订阅 dealloc 中的通知或niling 文本字段的委托。

那么,我的建议是 1)确保lvc在实例化另一个实例之前删除 ' 视图,以及 2)确保你PasscodeViewController的 'dealloc方法清除可能存在的悬空引用NSNotificationCenter

你的applicationWillEnterForeground:方法变成:

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];

    if (self.gbooShowingGetStartedPasswordAsk == FALSE) {

        NSString *strPassword = [myDefaults stringForKey:@"pass"];
        if (strPassword == nil) {
            strPassword = @"";
        }

        if (![strPassword isEqualToString:@""]) {
            [lvc.view removeFromSuperview]; // This line prevents the view from persisting
            lvc = [[PasscodeViewController alloc] 
                   initWithNibName:@"PasscodeView" bundle:nil];
            int th = self.window.frame.size.height;
            lvc.view.frame = CGRectMake(0, 20, 320, th); 

            lvc.view.backgroundColor = [UIColor groupTableViewBackgroundColor];

            [window addSubview:lvc.view];
        }
    }
}

PasscodeViewControllerdealloc应该是这样的:

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    if (txtPassword.delegate == self) {
        txtPassword.delegate = nil; //docs indicate this is still an `assign` property so is not auto-zeroing in ARC and iOS 5+
    }
}

通过这些更改,应该不会再有对已释放视图控制器的悬空引用,并且视图层次结构中也不会出现孤立视图。

于 2013-03-03T22:20:31.003 回答
0
txtPassword = [[UITextField alloc] initWithFrame:frame];

[self.view removeFromSuperview];

看起来对我很可疑。我不是 ARC 粉丝,但当您从其父视图中删除视图时,您似乎在合理地释放 UITextField。它可以出现在

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
 replacementString:(NSString *)string

称呼。

因此,您应该在 viewDidLoad 上初始化并分配您的 txtPassword,而不是将其分配到单元初始化中。

就像您说的那样,您要离开并从后台重新启动您的应用程序。如果您的单元格已被释放,您可以重新分配新的 txtPassword,但仍将旧的保留在最后一个单元格中。因此,您可能可以拥有多个具有相同委托分配的 txtPassword。如果您的视图未显示,则 txtPassword 的引用将不再有效,然后您才能返回 UITextfield 方法。

所以就像我说的,一定要分配你的 txtPassword 一次。所以把alloc放到viewDidLoad中,在viewDidUnLoad中释放。

记住我只是猜测。希望它会帮助你。

于 2013-02-26T16:31:20.860 回答