views:

48

answers:

1

I use SQLite Persistence Object in my App. It's working well on simulator but when i deploy on the device. Sometime it's work, sometime not. My app often is crashed and i got:"Program received signal: “EXC_BAD_ACCESS”." on Debugger Console.

In the Debugger i got:

#0 0x3153311c in sqlite3_blob_write
#1 0x31501b7c in sqlite3_os_end
#2 0x31527050 in sqlite3_open16
#3 0x3152720c in sqlite3_open16
#4 0x31528874 in sqlite3_backup_step
#5 0x31541224 in sqlite3_prepare16
#6 0x314fe948 in sqlite3_step
#7 0x314febb0 in sqlite3_exec
#8 0x0005261c in -[SQLitePersistentObject save] at SQLitePersistentObject.m:774
#9 0x00043da2 in -[DetailViewController finishEditTitle] at DetailViewController.m:324

or

#0 0x31503034 in sqlite3_enable_shared_cache
#1 0x31509580 in sqlite3_file_control
#2 0x3151ee34 in sqlite3_backup_init
#3 0x315236cc in sqlite3_open16
#4 0x31542a38 in sqlite3_prepare16
#5 0x314fe948 in sqlite3_step
#6 0x00052540 in -[SQLitePersistentObject save] at SQLitePersistentObject.m:768
#7 0x00043da2 in -[DetailViewController finishEditTitle] at DetailViewController.m:324

Here's my method [DetailViewController finishEditTitle]:

- (IBAction) finishEditTitle {
    if ([todologs.todo.title length] == 0) {
        if ([editTitleTextField.text length] > 0) 
            todologs.todo.title = editTitleTextField.text;
        else
            todologs.todo.title = @"New Todo";

        [todologs save]; // I GOT ERROR HERE
        todologs.todoid = todologs.todo.pk;
    } else {
        if ([editTitleTextField.text length] > 0) 
            todologs.todo.title = editTitleTextField.text;

    } 


    [AppNotification postNotification:kAppUpdateDateModified object:self userInfo:nil];

    self.navigationItem.titleView = titleButton;
    [todologs save];
    [editTitleView setHidden:NO];
}

and method [SQLitePersistenceObject save]:

-(void)save
{
    if (alreadySaving)
        return;
    alreadySaving = YES;

    [[self class] tableCheck];

    if (pk == 0)
    {
        NSLog(@"Object of type '%@' seems to be uninitialised, perhaps init does not call super init.", [[self class] description] );
        return;
    }

    NSDictionary *props = [[self class] propertiesWithEncodedTypes];

    if (!dirty)
    {
        // Check child and owned objects to see if any of them are dirty
        // Just tell children and composed objects to save themselves

        for (NSString *propName in props)
        {
            NSString *propType = [props objectForKey:propName];
            //int colIndex = sqlite3_bind_parameter_index(stmt, [[propName stringAsSQLColumnName] UTF8String]);
            id theProperty = [self valueForKey:propName];
            if ([propType hasPrefix:@"@"] ) // Object
            {
                NSString *className = [propType substringWithRange:NSMakeRange(2, [propType length]-3)];


                if (! (isCollectionType(className)) )
                {
                    if ([[theProperty class] isSubclassOfClass:[SQLitePersistentObject class]])
                        if ([theProperty isDirty])
                            dirty = YES;
                }
                else
                {
                    if (isNSSetType(className) || isNSArrayType(className))
                        for (id oneObject in (NSArray *)theProperty)
                            if ([oneObject isKindOfClass:[SQLitePersistentObject class]])
                                if ([oneObject isDirty])
                                    dirty = YES;                    
                                else if (isNSDictionaryType(className))
                                {
                                    for (id oneKey in [theProperty allKeys])
                                    {
                                        id oneObject = [theProperty objectForKey:oneKey];
                                        if ([oneObject isKindOfClass:[SQLitePersistentObject class]])
                                            if ([oneObject isDirty])
                                                dirty = YES;
                                    }
                                }
                }
            }
        }
    }

    NSArray *theTransients = [[self class] transients];

    if (dirty)
    {
        dirty = NO;

        sqlite3 *database = [[SQLiteInstanceManager sharedManager] database];

        // If this object is new, we need to figure out the correct primary key value, 
        // which will be one higher than the current highest pk value in the table.

        if (pk < 0)
        {
            NSString *pkQuery = [NSString stringWithFormat:@"SELECT SEQ FROM SQLITESEQUENCE WHERE NAME='%@'", [[self class] tableName]];
            sqlite3_stmt *statement;
            if (sqlite3_prepare_v2(database, [pkQuery UTF8String], -1, &statement, nil) == SQLITE_OK) 
            {
                if (sqlite3_step(statement) == SQLITE_ROW)    // I GOT ERROR HERE
                {
                    pk = sqlite3_column_int(statement, 0)+1;
                    char* errmsg;

                    NSString *seqIncrementQuery = [NSString stringWithFormat:@"UPDATE SQLITESEQUENCE set seq=%d WHERE name='%@'", pk, [[self class] tableName]];
                    if (sqlite3_exec (database, [seqIncrementQuery UTF8String], NULL, NULL, &errmsg) != SQLITE_OK)      
                        NSLog(@"Error Message: %s", errmsg);
                }

            }
            else 
                NSLog(@"Error determining next PK value in table %@", [[self class] tableName]);

            sqlite3_finalize(statement);
        }

        NSMutableString *updateSQL = [NSMutableString stringWithFormat:@"INSERT OR REPLACE INTO %@ (pk", [[self class] tableName]];

        NSMutableString *bindSQL = [NSMutableString string];

        for (NSString *propName in props)
        {
            if ([theTransients containsObject:propName]) continue;

            NSString *propType = [props objectForKey:propName];
            NSString *className = @"";
            if ([propType hasPrefix:@"@"])
                className = [propType substringWithRange:NSMakeRange(2, [propType length]-3)];
            if (! (isCollectionType(className)))
            {
                [updateSQL appendFormat:@", %@", [propName stringAsSQLColumnName]];
                [bindSQL appendString:@", ?"];
            }
        }

        [updateSQL appendFormat:@") VALUES (?%@)", bindSQL];

        sqlite3_stmt *stmt;
        int result = sqlite3_prepare_v2( database, [updateSQL UTF8String], -1, &stmt, nil);

        // if sql statement bound ok, now bind the column values
        if (result == SQLITE_OK)
        {
            int colIndex = 1;
            sqlite3_bind_int(stmt, colIndex++, pk);

            for (NSString *propName in props)
            {
                if ([theTransients containsObject:propName]) continue;

                NSString *propType = [props objectForKey:propName];
                NSString *className = propType;
                if ([propType hasPrefix:@"@"])
                    className = [propType substringWithRange:NSMakeRange(2, [propType length]-3)];
                //int colIndex = sqlite3_bind_parameter_index(stmt, [[propName stringAsSQLColumnName] UTF8String]);
                id theProperty = [self valueForKey:propName];
                if (theProperty == nil && ! (isCollectionType(className)))
                {
                    sqlite3_bind_null(stmt, colIndex++);
                }
                else if ([propType isEqualToString:@"i"] || // int
                         [propType isEqualToString:@"I"] || // unsigned int
                         [propType isEqualToString:@"l"] || // long
                         [propType isEqualToString:@"L"] || // usigned long
                         [propType isEqualToString:@"q"] || // long long
                         [propType isEqualToString:@"Q"] || // unsigned long long
                         [propType isEqualToString:@"s"] || // short
                         [propType isEqualToString:@"S"] || // unsigned short
                         [propType isEqualToString:@"B"] || // bool or _Bool
                         [propType isEqualToString:@"f"] || // float
                         [propType isEqualToString:@"d"] )  // double
                {
                    sqlite3_bind_text(stmt, colIndex++, [[theProperty stringValue] UTF8String], -1, NULL);
                }   
                else if ([propType isEqualToString:@"c"] || // char
                         [propType isEqualToString:@"C"] ) // unsigned char

                {
                    NSString *theString = [theProperty stringValue];
                    sqlite3_bind_text(stmt, colIndex++, [theString UTF8String], -1, NULL);
                }
                else if ([propType hasPrefix:@"@"] ) // Object
                {
                    NSString *className = [propType substringWithRange:NSMakeRange(2, [propType length]-3)];


                    if (! (isCollectionType(className)) )
                    {

                        if ([[theProperty class] isSubclassOfClass:[SQLitePersistentObject class]])
                        {
                            [theProperty save];
                            sqlite3_bind_text(stmt, colIndex++, [[theProperty memoryMapKey] UTF8String], -1, NULL);
                        }
                        else if ([[theProperty class] shouldBeStoredInBlob])
                        {
                            NSData *data = [theProperty sqlBlobRepresentationOfSelf];
                            sqlite3_bind_blob(stmt, colIndex++, [data bytes], [data length], NULL);
                        }
                        else
                        {
                            sqlite3_bind_text(stmt, colIndex++, [[theProperty sqlColumnRepresentationOfSelf] UTF8String], -1, NULL);
                        }
                    }
                    else
                    {
                        // Too difficult to try and figure out what's changed, just wipe rows and re-insert the current data.
                        NSString *xrefDelete = [NSString stringWithFormat:@"delete from %@_%@ where parent_pk = %d", [[self class] tableName], [propName stringAsSQLColumnName], pk];
                        char *errmsg = NULL;
                        if (sqlite3_exec (database, [xrefDelete UTF8String], NULL, NULL, &errmsg) != SQLITE_OK)
                            NSLog(@"Error deleting child rows in xref table for array: %s", errmsg);
                        sqlite3_free(errmsg);


                        if (isNSArrayType(className))
                        {
                            int arrayIndex = 0;
                            for (id oneObject in (NSArray *)theProperty)
                            {
                                if ([oneObject isKindOfClass:[SQLitePersistentObject class]])
                                {
                                    [oneObject save];
                                    NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, array_index, fk, fk_table_name) values (%d, %d, %d, '%@')", [[self class] tableName], [propName stringAsSQLColumnName],  pk, arrayIndex++, [oneObject pk], [[oneObject class] tableName]];
                                    if (sqlite3_exec (database, [xrefInsert UTF8String], NULL, NULL, &errmsg) != SQLITE_OK)
                                        NSLog(@"Error inserting child rows in xref table for array: %s", errmsg);
                                    sqlite3_free(errmsg);
                                }
                                else 
                                {
                                    if ([[oneObject class] canBeStoredInSQLite])
                                    {
                                        NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, array_index, object_data, object_class) values (%d, %d, ?, '%@')", [[self class] tableName], [propName stringAsSQLColumnName], pk, arrayIndex++, [oneObject className]];

                                        sqlite3_stmt *xStmt;
                                        if (sqlite3_prepare_v2( database, [xrefInsert UTF8String], -1, &xStmt, nil) == SQLITE_OK)
                                        {
                                            if ([[oneObject class] shouldBeStoredInBlob])
                                            {
                                                NSData *data = [oneObject sqlBlobRepresentationOfSelf];
                                                sqlite3_bind_blob(xStmt, 1, [data bytes], [data length], NULL);
                                            }
                                            else
                                            {
                                                sqlite3_bind_text(xStmt, 1, [[oneObject sqlColumnRepresentationOfSelf] UTF8String], -1, NULL);  
                                            }

                                            if (sqlite3_step(xStmt) != SQLITE_DONE)
                                                NSLog(@"Error inserting or updating cross-reference row");
                                            sqlite3_finalize(xStmt);
                                        }
                                    }
                                    else 
                                        NSLog(@"Could not save object at array index: %d", arrayIndex++);
                                }
                            }
                        }
                        else if (isNSDictionaryType(className))
                        {
                            for (NSString *oneKey in (NSDictionary *)theProperty)
                            {
                                id oneObject = [(NSDictionary *)theProperty objectForKey:oneKey];
                                if ([(NSObject *)oneObject isKindOfClass:[SQLitePersistentObject class]])
                                {
                                    [(SQLitePersistentObject *)oneObject save];
                                    NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, dictionary_key, fk, fk_table_name) values (%d, '%@', %d, '%@')",  [[self class] tableName], [propName stringAsSQLColumnName], pk, oneKey, [(SQLitePersistentObject *)oneObject pk], [[oneObject class] tableName]];
                                    if (sqlite3_exec (database, [xrefInsert UTF8String], NULL, NULL, &errmsg) != SQLITE_OK)
                                        NSLog(@"Error inserting child rows in xref table for array: %s", errmsg);
                                    sqlite3_free(errmsg);
                                }
                                else
                                {
                                    if ([[oneObject class] canBeStoredInSQLite])
                                    {
                                        NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, dictionary_key, object_data, object_class) values (%d, '%@', ?, '%@')", [[self class] tableName], [propName stringAsSQLColumnName], pk, oneKey, [oneObject className]];
                                        sqlite3_stmt *xStmt;
                                        if (sqlite3_prepare_v2( database, [xrefInsert UTF8String], -1, &xStmt, nil) == SQLITE_OK)
                                        {
                                            if ([[oneObject class] shouldBeStoredInBlob])
                                            {
                                                NSData *data = [oneObject sqlBlobRepresentationOfSelf];
                                                sqlite3_bind_blob(xStmt, 1, [data bytes], [data length], NULL);
                                            }
                                            else
                                                sqlite3_bind_text(xStmt, 1, [[oneObject sqlColumnRepresentationOfSelf] UTF8String], -1, NULL);

                                            if (sqlite3_step(xStmt) != SQLITE_DONE)
                                                NSLog(@"Error inserting or updating cross-reference row");

                                            sqlite3_finalize(xStmt);

                                        }
                                    }
                                }
                            }
                        }
                        else // NSSet
                        {
                            for (id oneObject in (NSSet *)theProperty)
                            {
                                if ([oneObject isKindOfClass:[SQLitePersistentObject class]])
                                {
                                    [oneObject save];
                                    NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, fk, fk_table_name) values (%d, %d, '%@')", [[self class] tableName], [propName stringAsSQLColumnName],  pk, [oneObject pk], [[oneObject class] tableName]];
                                    if (sqlite3_exec (database, [xrefInsert UTF8String], NULL, NULL, &errmsg) != SQLITE_OK)
                                        NSLog(@"Error inserting child rows in xref table for array: %s", errmsg);
                                    sqlite3_free(errmsg);
                                }
                                else
                                {
                                    if ([[oneObject class] canBeStoredInSQLite])
                                    {
                                        NSString *xrefInsert = [NSString stringWithFormat:@"insert into %@_%@ (parent_pk, object_data, object_class) values (%d, ?, '%@')", [[self class] tableName], [propName stringAsSQLColumnName], pk, [oneObject className]];

                                        sqlite3_stmt *xStmt;
                                        if (sqlite3_prepare_v2( database, [xrefInsert UTF8String], -1, &xStmt, nil) == SQLITE_OK)
                                        {
                                            if ([[oneObject class] shouldBeStoredInBlob])
                                            {
                                                NSData *data = [oneObject sqlBlobRepresentationOfSelf];
                                                sqlite3_bind_blob(xStmt, 1, [data bytes], [data length], NULL);
                                            }
                                            else
                                                sqlite3_bind_text(xStmt, 1, [[oneObject sqlColumnRepresentationOfSelf] UTF8String], -1, NULL);

                                            if (sqlite3_step(xStmt) != SQLITE_DONE)
                                                NSLog(@"Error inserting or updating cross-reference row");

                                            sqlite3_finalize(xStmt);
                                        }
                                    }
                                    else 
                                        NSLog(@"Could not save object from set");
                                }
                            }
                        }
                    }
                }
            }
            if (sqlite3_step(stmt) != SQLITE_DONE)
                NSLog(@"Error inserting or updating row");
            sqlite3_finalize(stmt);
        }
        else
            NSLog(@"Error preparing save SQL: %s", sqlite3_errmsg(database));
        // Can't register in memory map until we have PK, so do that now.
        if (![[objectMap allKeys] containsObject:[self memoryMapKey]])
            [[self class] registerObjectInMemory:self];


    }

    alreadySaving = NO;
}