Программа получила сигнал «EXC_Bad_Access» NSMutableArray

#iphone #objective-c #arrays #ios

#iPhone #objective-c #массивы #iOS

Вопрос:

если я попытаюсь повторно запустить приведенный ниже код, я получу сообщение EXE_bad_access о [количестве списков категорий]

 NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
NSLog(@"number of items is %@", [categoryList count]);
  

Класс находится ниже

 #import "CategoryItem.h"

#import "SQLite.h"

@interface CategoryItem : NSObject {
    NSInteger ID;
    NSInteger SortOrder;
    NSString *Name;
    NSString *ShoppingImage;

}

@property (nonatomic, nonatomic) NSInteger SortOrder;
@property (nonatomic, retain) NSString * Name;
@property (nonatomic, retain) NSString * ShoppingImage;
@property (nonatomic, nonatomic) NSInteger ID;

- (id)initWithObject:(NSInteger)itemID;
-(NSMutableArray *)getAll;

@end
@implementation CategoryItem


@synthesize ShoppingImage;
@synthesize Name;
@synthesize ID;
@synthesize SortOrder;

- (id)initWithObject:(NSInteger)itemID {

    if ((self = [super init])) {
        sqlite3 *database;
        // Open the database. The database was prepared outside the application.
        if (sqlite3_open([[SQLite fullFilePath] UTF8String], amp;database) == SQLITE_OK) {
            // Get the primary key for all books.
            const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem WHERE ID =?";
            sqlite3_stmt *statement;
            // Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
            // The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.        
            if (sqlite3_prepare_v2(database, sql, -1, amp;statement, NULL) == SQLITE_OK) {
                // We "step" through the results - once for each row.
                sqlite3_bind_int(statement, 1, itemID);

                while (sqlite3_step(statement) == SQLITE_ROW) {
                    // The second parameter indicates the column index into the result set.
                    self.ID = itemID;
                    self.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
                    self.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
                    self.SortOrder = sqlite3_column_int(statement, 3);
                }
            }
            // "Finalize" the statement - releases the resources associated with the statement.
            sqlite3_finalize(statement);
        } else {
            // Even though the open failed, call close to properly clean up resources.
            sqlite3_close(database);
            NSLog(@"Failed to open database with message '%s'.", sqlite3_errmsg(database));
            // Additional error handling, as appropriate...
        }

    }
    return self;
}

-(NSMutableArray*)getAll{

    NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];

    sqlite3 *database;
    // Open the database. The database was prepared outside the application.
    if (sqlite3_open([[SQLite fullFilePath] UTF8String], amp;database) == SQLITE_OK) {
        // Get the primary key for all books.
        const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem ORDER BY SortOrder";
        sqlite3_stmt *statement;
        // Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
        // The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.

        if (sqlite3_prepare_v2(database, sql, -1, amp;statement, NULL) == SQLITE_OK) 
        {

            // We "step" through the results - once for each row.
            while (sqlite3_step(statement) == SQLITE_ROW)
            {
                // The second parameter indicates the column index into the result set.

                CategoryItem *categoryItem = [[CategoryItem alloc] init];

                categoryItem.ID = sqlite3_column_int(statement, 0);
                categoryItem.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
                categoryItem.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
                categoryItem.SortOrder = sqlite3_column_int(statement, 3);

                [listArray addObject:categoryItem];

                [categoryItem release];
                categoryItem = nil;

            }


        }else{
            printf( "could not prepare statemnt: %sn", sqlite3_errmsg(database) ); 
        }
        // "Finalize" the statement - releases the resources associated with the statement.
        sqlite3_finalize(statement);

    } else {
        // Even though the open failed, call close to properly clean up resources.
        sqlite3_close(database);
        NSLog(@"Failed to open database with message '%s'.", sqlite3_errmsg(database));
        // Additional error handling, as appropriate...
    }

    //NSLog(@"this is the list array count %@", [listArray count]);

    return listArray;
}



- (void)dealloc {
    [super dealloc];
    [Name release];
    [ShoppingImage release];

}


@end
  

Комментарии:

1. Только мазохисты используют SQLite C API непосредственно в Objective-C. Вместо этого используйте FMDB (оболочку SQLite) или CoreData (диспетчер графов объектов).

2. @Luke разве ты не должен инициализировать это CategoryItem ? Вы вызываете, alloc но нет initWithObject

3. Вы не инициализировали класс categoryList, но вызываете для него метод. Кажется, проблема в (несуществующей) инициализации.

4. Действительно, всякий раз, когда вам приходится писать так много комментариев, чтобы вспомнить, что происходит, это признак того, что уровень абстракции может быть очень полезным.

5. Пожалуйста, попробуйте Core Data. Настройка примерно такая же, но работать с ней намного проще. Если вы все сделаете правильно, вы можете легко подготовить набор данных для отправки вместе с вашим приложением («База данных была подготовлена вне приложения»).

Ответ №1:

Кажется неправильным способ, которым вы создаете свой CategoryItem . Вы вызываете, alloc но не какой init... -либо метод. Возможно, вы захотите использовать initWithObject метод, который вы предоставили в своей реализации.

Из документов Apple:

Для создания объекта с использованием Objective-C. Требуется два шага. Вы должны:

  • Динамически выделять память для нового объекта

  • Инициализируйте вновь выделенную память соответствующими значениями

Объект не будет полностью функционировать, пока не будут выполнены оба шага. Каждый шаг выполняется отдельным методом, но обычно в одной строке кода:

идентификатор объекта = [[Выделение прямоугольника] инициализация];

Редактировать:

Помимо проблемы инициализации, похоже, существует концептуальная проблема (на которую указал @Terry Wilcox): вызов метода getAll в экземпляре, похоже, не имеет смысла и поэтому вместо этого должен быть определен как метод класса:

   (NSMutableArray*)getAll;
  

и должен вызываться следующим образом:

 NSMutableArray *categoryList = [CategoryItem getAll];
  

РЕДАКТИРОВАТЬ 2:

Ваше заявление журнала также не кажется правильным. [categoryList count] возвращает NSUInteger , с помощью которого вы пытаетесь распечатать объект %@ . Используйте %i вместо:

 NSLog(@"number of items is %i", [categoryList count]);
  

Комментарии:

1. Код не пытается создать CategoryItem, он пытается создать NSMutableArray.

2. @Luke Вы также пытаетесь зарегистрировать int, как если бы это был объект (см. РЕДАКТИРОВАНИЕ 2)

3. боже, это была сама инструкция журнала. Большое спасибо, вчера я удалил инструкцию log, и она начала работать, даже не задумываясь об этом. Что ж, это час моей жизни, который я никогда не верну

Ответ №2:

Этот код:

 NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
  

не имеет смысла. Если GetAll является методом класса в CategoryItem, то он должен быть определен как

   (NSMutableArray*)getAll;
  

и вы должны назвать это как

 NSMutableArray *categoryList = [CategoryItem getAll];
  

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

Комментарии:

1. спасибо за этот гораздо более чистый код!все еще не решает мои проблемы с памятью

2. Это не чистый код, это рабочий код. Ваш исходный код не будет работать должным образом, он не вызывает init, поэтому ваш метод GetAll может ничего не возвращать.