How optimize code with introspection + heavy alloc on iPhone
- by mamcx
I have a problem. I try to display a UITable that could have 2000-20000 records (typicall numbers.)
I have a SQLite database similar to the Apple contacts application.
I do all the tricks I know to get a smoth scroll, but I have a problem.
I load the data in 50 recods blocks. Then, when the user scroll, request next 50 until finish the list.
However, load that 50 records cause a notable "pause" in loading and scrolling. Everything else works fine.
I cache the data, have opaque cells, draw it by code, etc...
I swap the code loading the same data in dicts and have a performance boost but wonder if I could keep my object oriented aproach and improve the actual code.
This is the code I think have the performance problem:
-(NSArray *) loadAndFill: (NSString *)sql theClass: (Class)cls {
[self openDb];
NSMutableArray *list = [NSMutableArray array];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DbObject *ds;
Class myClass = NSClassFromString([DbObject getTableName:cls]);
FMResultSet *rs = [self load:sql];
while ([rs next]) {
ds = [[myClass alloc] init];
NSDictionary *props = [ds properties];
NSString *fieldType = nil;
id fieldValue;
for (NSString *fieldName in [props allKeys]) {
fieldType = [props objectForKey: fieldName];
fieldValue = [self ValueForField:rs Name:fieldName Type:fieldType];
[ds setValue:fieldValue forKey:fieldName];
}
[list addObject :ds];
[ds release];
}
[rs close];
[pool drain];
return list;
}
And I think the main culprit is:
-(id) ValueForField: (FMResultSet *)rs Name:(NSString *)fieldName Type:(NSString *)fieldType {
id fieldValue = nil;
if ([fieldType isEqualToString:@"i"] || // int
[fieldType isEqualToString:@"I"] || // unsigned int
[fieldType isEqualToString:@"s"] || // short
[fieldType isEqualToString:@"S"] || // unsigned short
[fieldType isEqualToString:@"f"] || // float
[fieldType isEqualToString:@"d"] ) // double
{
fieldValue = [NSNumber numberWithInt: [rs longForColumn:fieldName]];
}
else if ([fieldType isEqualToString:@"B"]) // bool or _Bool
{
fieldValue = [NSNumber numberWithBool: [rs boolForColumn:fieldName]];
}
else if ([fieldType isEqualToString:@"l"] || // long
[fieldType isEqualToString:@"L"] || // usigned long
[fieldType isEqualToString:@"q"] || // long long
[fieldType isEqualToString:@"Q"] ) // unsigned long long
{
fieldValue = [NSNumber numberWithLong: [rs longForColumn:fieldName]];
}
else if ([fieldType isEqualToString:@"c"] || // char
[fieldType isEqualToString:@"C"] ) // unsigned char
{
fieldValue = [rs stringForColumn:fieldName];
//Is really a boolean?
if ([fieldValue isEqualToString:@"0"] || [fieldValue isEqualToString:@"1"]) {
fieldValue = [NSNumber numberWithInt: [fieldValue intValue]];
}
}
else if ([fieldType hasPrefix:@"@"] ) // Object
{
NSString *className = [fieldType substringWithRange:NSMakeRange(2, [fieldType length]-3)];
if ([className isEqualToString:@"NSString"]) {
fieldValue = [rs stringForColumn:fieldName];
}
else if ([className isEqualToString:@"NSDate"]) {
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"];
NSString *theDate = [rs stringForColumn:fieldName];
if (theDate) {
fieldValue = [dateFormatter dateFromString: theDate];
}
else
{
fieldValue = nil;
}
[dateFormatter release];
}
else if ([className isEqualToString:@"NSInteger"]) {
fieldValue = [NSNumber numberWithInt: [rs intForColumn :fieldName]];
}
else if ([className isEqualToString:@"NSDecimalNumber"]) {
fieldValue = [rs stringForColumn :fieldName];
if (fieldValue) {
fieldValue = [NSDecimalNumber decimalNumberWithString:[rs stringForColumn :fieldName]];
}
}
else if ([className isEqualToString:@"NSNumber"]) {
fieldValue = [NSNumber numberWithDouble: [rs doubleForColumn:fieldName]];
}
else
{
//Is a relationship one-to-one?
if (![fieldType hasPrefix:@"NS"]) {
id rel = class_createInstance(NSClassFromString(className), sizeof(unsigned));
Class theClass = [rel class];
if ([rel isKindOfClass:[DbObject class]]) {
fieldValue = [rel init];
//Load the record...
NSInteger Id = [rs intForColumn:[theClass relationName]];
if (Id>0) {
[fieldValue release];
Db *db = [Db currentDb];
fieldValue = [db loadById: theClass theId:Id];
}
}
} else {
NSString *error = [NSString stringWithFormat:@"Err Can't get value for field %@ of type %@", fieldName, fieldType];
NSLog(error);
NSException *e = [NSException
exceptionWithName:@"DBError"
reason:error
userInfo:nil];
@throw e;
}
}
}
return fieldValue;
}