如何在iPhone上使用内省+重分配来优化代码

时间:2009-04-09 04:16:46

标签: objective-c performance

我有问题。我尝试显示可能有2000-20000条记录的UITable(典型数字。)

我有一个类似于Apple联系人应用程序的SQLite数据库。

我做了我知道的所有技巧以获得一个smoth卷轴,但我有一个问题。

我将数据加载到50个recods块中。然后,当用户滚动时,请求下一个50直到完成列表。

但是,加载50条记录会导致加载和滚动时出现明显的“暂停”。其他一切都很好。

我缓存数据,拥有不透明的单元格,通过代码绘制等等......

我在代码中交换加载相同数据的代码,并且性能提升但是想知道我是否可以保持面向对象的方法并改进实际的代码。

这是我认为存在性能问题的代码:

    -(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;
    }

我认为主要罪魁祸首是:

    -(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;
    }

2 个答案:

答案 0 :(得分:0)

您应该能够使用“仪器”使用“仪器”运行此程序,以找出问题所在。您可以将ValueForField重写为较小的调用,以查看它的哪个部分是瓶颈,如果是necc。

其他可能性: 如果数据是静态的,你可以将多个(全部?)加载到C数组中(特别是对于int和bools)。如果有许多相同的值,您可以共享相似的对象 - 例如:如果表有20,000行,并且18,000对于某些列具有相同的字符串,则可以只创建一个字符串并共享它。

答案 1 :(得分:0)

看起来性能问题是使用decimalNumberWithString。

如果我从代码中删除它,延迟比用它小很多。

我需要NSDecimalNumber用于货币管理是不好的:(