1

次のエラーが表示されます。

EXC_BAD_ACCESS  
KERN_INVALID_ADDRESS 
[UITextField keyboardInputChangedSelection:]

この最新のクラッシュ レポートまで、このクラッシュを再現できませんでした。

アプリでパスコード/ピンdidFinishLaunchingWithOptionsが設定されているかどうかにapplicationWillEnterForeground応じて、このView Controllerを呼び出します。

ここで奇妙なことです。

クラッシュは、次のシナリオで発生します。

  1. ピンを設定せずにアプリを実行し、ピンを設定します。
  2. 次にホームボタンを押します。
  3. このView Controllerがポップアップし、ピンを入力するとビューが閉じます。
  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

lvcがではなくnil、スーパービューがあり、applicationWillEnterForeground:が呼び出され、そこでコードのメインブロックに分岐する場合を考えてみます。lvcの新しいインスタンスに割り当てるとPasscodeViewController、の古い値のlvc割り当てが解除される可能性があります。ただし、そのviewプロパティにはスーパービューがあるため、一緒に割り当てが解除されることはありません。これらの生きているオブジェクトは、割り当て解除されたViewControllerがまだリッスンしている通知またはアクションを生成する可能性があります。また、deallocnilの通知の購読を解除したり、テキストフィールドのデリゲートを購読したりしているとは限らないことにも気づきました。

したがって、私の提案は、1)lvc別のインスタンスをインスタンス化する前に、ビューを削除することを確認し、2)メソッドがぶら下がっている可能性PasscodeViewControllerのある参照をクリーンアップすることを確認することです。deallocNSNotificationCenter

あなたの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+
    }
}

これらの変更により、割り当てが解除されたView Controllerへのぶら下がっている参照がなくなり、ビュー階層に孤立したビューがなくなります。

于 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 を持つことができます。ビューが非表示の場合、UITextfield メソッドを返す前に、txtPassword の参照は無効になります。

私が言ったように、txtPassword を一度だけ割り当てるようにしてください。したがって、それをviewDidLoadに割り当て、viewDidUnLoadで解放します。

私は推測しているだけであることを忘れないでください。それがあなたを助けることを願っています。

于 2013-02-26T16:31:20.860 に答える