EXC_BAD_ACCESS, когда я закрываю свое окно, которое также является делегатом моего приложения - PullRequest
0 голосов
/ 04 декабря 2011

Я написал приложение Какао, и я получаю ошибку EXC_BAD_ACCESS при закрытии окна приложения.Я прочитал, что эта ошибка обычно означает проблемы с памятью, но у меня включено ARC mode, и мне не нужно заботиться об освобождении и т. Д. (XCode запрещает мне вызывать эти функции и управлять памятью автоматически).

Ошибкауказывая на строку return NSApplicationMain(argc, (const char **)argv); в основной функции.

Вот код моего приложения:

.h файл:

@interface MainDreamer : NSWindow <NSWindowDelegate> 
{    
    NSTextField *dreamField;
    NSTableView *dreamTable;    
    NSImageView *dreamview;

    NSMutableArray *dreamlist;  
    NSMutableArray *dataset;
}

@property (nonatomic, retain) IBOutlet NSTextField *dreamField;
@property (nonatomic, retain) IBOutlet NSTableView *dreamTable;
@property (nonatomic, retain) IBOutlet NSImageView *dreamview;
@property (nonatomic, retain) IBOutlet NSMutableArray *dreamlist;
@property (nonatomic, retain) IBOutlet NSMutableArray *dataset;
@property (assign) IBOutlet NSWindow *window;

@end

.m файл:

@implementation MainDreamer

@synthesize window;
@synthesize dataset;
@synthesize dreamField;
@synthesize dreamlist;
@synthesize dreamview;
@synthesize dreamTable;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
    NSString *applicationPath = [[NSBundle mainBundle] bundlePath];
    NSString *filename = [applicationPath stringByAppendingPathComponent:@"dreams"];
    NSLog(self.description);

    dreamlist = [[NSMutableArray alloc] init];  
    dataset = [[NSMutableArray alloc] init];
    dataset = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
    if([dataset count] != 0) {
        int i = 0;
        while (i < [dataset count]) { 
            Dream *dr = [[Dream alloc] init];
            dr = [dataset objectAtIndex:i];
            [dreamlist addObject: dr.dreamname];         
            i++;
        }
    }    
    [dreamTable reloadData]; 
}

-(void)applicationWillTerminate:(NSNotification *)notification{       
    NSString *applicationPath = [[NSBundle mainBundle] bundlePath];
    NSString *filename = [applicationPath stringByAppendingPathComponent:@"dreams"];
    [NSKeyedArchiver archiveRootObject:dataset toFile:filename];
    NSLog(@"finish");
}

- (void) mouseUp:(NSEvent *)theEvent{
    long index = [dreamTable selectedRow];
    Dream *dr = [[Dream alloc] init];
    dr = [dataset objectAtIndex:index];
    dr.dreampicture = dreamview.image;
    [dataset replaceObjectAtIndex:index withObject:dr];
    NSLog(self.description);
}

- (void) tableViewSelectionDidChange: (NSNotification *) notification{
    long row = [dreamTable selectedRow];
    Dream *dr = [[Dream alloc] init];
    dr = [dataset objectAtIndex: row];
    if(dr.dreampicture != NULL) 
        dreamview.image = dr.dreampicture;
    NSLog(@"selected row changed");
}

Класс "Мечта":

@interface Dream : NSObject <NSCoding>
{
    NSString *dreamname;
    NSImage *dreampicture;
}

@property (retain) NSString* dreamname;
@property (retain) NSImage* dreampicture;

-(id)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;

@end

Что не так, почему происходит EXC_BAD_ACCESS Напоминаю, что у меня есть xCode 4 с автоматическим подсчетом ссылок (ARC)

Спасибо

ОБНОВЛЕНИЕ

Я использовал Профиль, чтобы найти событие зомби.Итак, я выяснил это: Сообщение Objective C было отправлено на освобожденный объект (зомби (по адресу 0x108d85230)

Ответственный абонент - [NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed: saveWindows:]

У меня былоэта функция в коде:

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender{
    return TRUE;
}

Однако после того, как я поместил ее в комментарии, это событие зомби продолжает происходить.

Ответы [ 3 ]

3 голосов
/ 04 декабря 2011

Это неправильно:

dataset = [[NSMutableArray alloc] init]; // WRONG
dataset = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];

Почему?Сначала вы выделяете пустой массив и сохраняете его в переменной экземпляра dataset.Но в следующей строке вы заменяете пустой массив тем, что возвращает +unarchiveObjectWithFile:.Почему это проблема?Что ж, если вы прочитаете документы, вы увидите, что он возвращает nil, если файл не найден.Это означает, что теперь вы заменяете пустой массив на nil, и все сообщения, которые вы отправляете на dataset, будут игнорироваться (сообщения на nil беззвучно игнорируются в Objective-C)

Я предполагаю, что вы действительно хотите загрузитьнабор данных из файла, и только если это не удалось, начните с пустого набора данных:

dataset = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
if (dataset==nil) dataset = [[NSMutableArray alloc] init];

У вас позже появится похожая ошибка:

Dream *dr = [[Dream alloc] init]; // WRONG
dr = [dataset objectAtIndex:index];

Вы создаете объект Dream изатем немедленно замените его чем-нибудь из набора данных.То, что вы на самом деле хотите сделать, это:

Dream *dr;
dr = [dataset objectAtIndex:index];

или короче:

Dream *dr = [dataset objectAtIndex:index];

Опять же, вы можете заменить цикл while на цикл fast-enumeration в стиле:

    for (Dream *dr in dataset) {
        [dreamlist addObject: dr.dreamname];         
    }

Наконец, чтобы добраться до точки, я не думаю, что EXC_BAD_ACCESS действительно происходит в main.h.Я предполагаю, что вы используете Xcode 4. Пожалуйста, используйте навигатор потока / стека в правой боковой панели при отладке, чтобы найти фактическую позицию, где происходит ошибка.

Возможно, что ошибка действительно возникает в applicationWillTerminate:, потому чтовы пытаетесь архивировать dataset, что, вероятно, nil, и, вероятно, не разрешено архивировать nil.

3 голосов
/ 05 декабря 2011

Сбой вызван тем, что вы сделали окно делегатом вашего приложения.Когда вы закрываете окно, это последний релиз, который убивает его, и если это последнее окно, которое у вас было, оно заставляет приложение спрашивать своего делегата, должно ли оно выйти.Поскольку окно, которое вы только что убили , является делегатом приложения, вы получите этот сбой.

Более подробное объяснение и предложение решения в моем ответе на ваш следующий вопрос.

1 голос
/ 04 декабря 2011

С ARC вы должны использовать strong и weak вместо retain и assign.

...