如果我在从委托访问我的视图时得到 EXC_BAD_ACCESS,这是不是意味着我有内存泄漏?

Posted

技术标签:

【中文标题】如果我在从委托访问我的视图时得到 EXC_BAD_ACCESS,这是不是意味着我有内存泄漏?【英文标题】:If I get EXC_BAD_ACCESS when accessing my view from a delegate, does that mean I have a memory leak?如果我在从委托访问我的视图时得到 EXC_BAD_ACCESS,这是否意味着我有内存泄漏? 【发布时间】:2011-11-11 16:55:54 【问题描述】:

我有一个对象,我 init 喜欢这样:

pdtd = [[ProfileDataTableDelegate alloc] initWithTableView:profileDataTableView scrollView:contentScrollView];

所以我的view 被传递给pdtd 对象。我现在可以毫无问题地注销我的观点。当我在文本字段内单击并告知滚动视图重新定位时,我得到 错误。在我的表委托 (pdtd) 上,我收到了错误,该错误在该函数的第一行给出了“EXC_BAD_ACCESS”:

- (void)keyboardDidShow:(NSNotification *)notif 
    [self.scrollView setContentOffset:CGPointMake(0, 100) animated:YES]; // <-- ERROR SHOWS UP HERE

    [self.scrollView setContentSize:CGSizeMake([self.scrollView frame].size.width, 
                                               [self.scrollView frame].size.height + 100)];

这很奇怪,因为我在另一个视图上执行此操作并且效果很好。我不确定出了什么问题或如何从调试器中获取更多信息。下面是相关类中的相关函数。

ProfileViewController.h

...

#import "ProfileDataTableDelegate.h"


@interface ProfileViewController : UIViewController <UINavigationControllerDelegate> 
    UITableView *profileDataTableView;
    ProfileDataTableDelegate *pdtd;
    UINavigationBar *navBar;
    UIScrollView *contentScrollView;


...

@property (nonatomic, retain) IBOutlet UITableView *profileDataTableView;
@property (nonatomic, retain) ProfileDataTableDelegate *pdtd;
@property (nonatomic, retain) IBOutlet UINavigationBar *navBar;
@property (nonatomic, retain) IBOutlet UIScrollView *contentScrollView;

@end

ProfileViewController.m

...

- (void)viewDidLoad 
    [super viewDidLoad];

    //nav title
    [navBar.topItem setTitle:@"Your Profile"];

    [self.navigationController setNavigationBarHidden:NO];

    // nav bar button
    UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(saveBtnTouched:)];
    saveButton.enabled = false;
    [navBar.topItem setRightBarButtonItem:saveButton];

    pdtd = [[ProfileDataTableDelegate alloc] initWithTableView:profileDataTableView scrollView:contentScrollView];

    void (^block)(BOOL) = ^(BOOL is_valid) 
        if(is_valid == YES) 
            [saveButton setEnabled:YES];
         else if(is_valid == NO) 
            [saveButton setEnabled:NO];
        
    ;

    [pdtd setValidatorBlock:block];


...

ProfileDateTableDelegate.h

...

#import "ValidatedTextViewTableCell.h"

typedef void (^ ValidatorBlock)(BOOL);

@interface ProfileDataTableDelegate : NSObject <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate> 
    UITableView *profileDataTableView;
    UIScrollView *scrollView;

        ...


- (id)initWithTableView:(UITableView *)tableView scrollView:(UIScrollView *)scrollView;

...

@property (nonatomic, retain) IBOutlet UITableView *profileDataTableView;
@property (nonatomic, retain) UIScrollView *scrollView;

@end

ProfileDateTableDelegate.m

...

@implementation ProfileDataTableDelegate

@synthesize profileDataTableView;
@synthesize profileDataTableLabels;
@synthesize scrollView;

@synthesize emailCell, phoneCell, nameCell;
@synthesize validatedTextViewTableCell;
@synthesize validatorBlock;


- (id)initWithTableView:(UITableView *)tableView scrollView:(UIScrollView *)view 
    self = [super init];

    if(self) 
        profileDataTableView = tableView;
        [profileDataTableView setDelegate:self];
        [profileDataTableView setDataSource:self];

        scrollView = view;

        profileDataTableLabels = [[NSArray alloc] initWithObjects:@"Name", @"Email", @"Phone", nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector (keyboardDidShow:)
                                                     name: UIKeyboardDidShowNotification object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self 
                                                 selector:@selector (keyboardDidHide:)
                                                     name: UIKeyboardDidHideNotification object:nil];
    

    return self;


...

- (void)keyboardDidShow:(NSNotification *)notif 
    //NSLog(@"keyboard did show %@", notif); // notif gives coordinates etc
    //[scrollView setContentOffset:CGPointMake(0, 480 - 372) animated:YES];

    [self.scrollView setContentOffset:CGPointMake(0, 100) animated:YES];

    [self.scrollView setContentSize:CGSizeMake([self.scrollView frame].size.width, 
                                               [self.scrollView frame].size.height + 100)];

    //[scrollView setFrame:CGRectMake(0, 0, 320, 320)];


    // disable scrolling because it resets the rect position
    //[scrollView setScrollEnabled:NO];


...

@end

我不确定如何获取堆栈跟踪信息 (Xcode 4.2)。此外,在分析中有几个警告,但没有什么严重的(例如蓝色)。

【问题讨论】:

【参考方案1】:

您需要在ProfileDateTableDelegate.minitWithTableView: 方法中调用self.scrollView = viewscrollView = [view retain]

基本上发生的情况是,您的 scrollView 在您能够访问它之前已在其他视图中被释放,因为您的“委托”类不会以任何方式保留它,即使您在您的财产上指定了 nonatomic,retain .由于您直接在 init 中设置 ivar 而不是使用 setter 属性,因此不会在其上调用保留。直接使用 ivar 时,如果要保留它,必须自己调用retain

避免此错误的一种方法是在您的 ivars 之前或之后使用下划线 _(Apple 使用 before,所以我使用 after 以避免私人 ivar 冲突),如下所示:

在.h中:

@interface
    UIScrollView *scrollView_;


@property (nonatomic,retain) UIScrollView *scrollView;

在 *.m 中:

@synthesize scrollView = scrollView_;

那么,你在init,你可以像这样保留进来的视图:

scrollView_ = [view retain];

只要确保你在dealloc中正确发布它:

- (void)dealloc
    [scrollView_ release], scrollView_ = nil;
    [super dealloc];

你可以找到更多关于这一切here的信息。

【讨论】:

【参考方案2】:

啊,可怕的EXC_BAD_ACCESS。无需浏览所有代码,这里有一个提示可以让您立即开始。运行分析器。然后,使用 Instruments 中的 Leaks 工具。

有关详细信息,以及有关导致这些错误的原因的出色链接以及如何修复的分步说明,请查看这些问题的答案:

finding reason for EXC_BAD_ACCESS - in Xcode4

Random EXC_BAD_ACCESS in a place that it cannot happen

【讨论】:

以上是关于如果我在从委托访问我的视图时得到 EXC_BAD_ACCESS,这是不是意味着我有内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

快速访问应用程序委托变量延迟视图加载

如何删除此 EXC_BAD 访问错误?

委托创建 ViewController 的新实例

从 UITableView 委托方法中访问实例变量时获取“EXC_BAD_ACCESS”

如何在从父视图过滤时重新渲染完整日历

无法导航到根视图控制器