Optimizing for a loop in Objective-C

Just find some simple tips on how to better optimize the for loop ( in terms of memory usage ) in Obj-C and ARC, taking this unoptimized arbitrary code as:

NSMutableArray *array = [NSMutableArray array];

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    NSString *first = [dict valueForKeyPath:@"object.first"];
    NSString *second = [dict valueForKeyPath:@"object.second"];
    NSString *third = [dict valueForKeyPath:@"object.third"];

    [array addObject:@[first, second, third]];

}

      

Which one is better / right in practice (assuming many loops can make a difference)

1) Declare once, copy to array.

NSMutableArray *array = [NSMutableArray array];
NSString *first, *second, *third;

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    first = [dict valueForKeyPath:@"object.first"];
    second = [dict valueForKeyPath:@"object.second"];
    third = [dict valueForKeyPath:@"object.third"];

    [array addObject:@[[first copy], [second copy], [third copy]]];
    first, second, third = nil;

}

      

2) Autoreleasepool

NSMutableArray *array = [NSMutableArray array];

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    @autoreleasepool {
        NSString *first = [dict valueForKeyPath:@"object.first"];
        NSString *second = [dict valueForKeyPath:@"object.second"];
        NSString *third = [dict valueForKeyPath:@"object.third"];

        [array addObject:@[first, second, third]];
    }

}

      

3) Anything else?

+3


source to share


2 answers


first

, second

And third

go beyond the cycle in all situations regardless of whether they are in dict

. The array is @[first, second, third]

retained regardless of whether it is added to the array.

Hence, there will be a very limited difference between the three. valueForKeyPath:

does not create a copy, so all you are talking about is (i) storing in the table count count *; and (ii) storage in the auto resource pool.

(i) is not just negligible, but so intrinsic to the current runtime implementation that there is no point in relying on it. (ii) is also negligible, but clearly required by the specification.

Technically @autoreleasepool

, it will probably be a little more compact (depending on the total size of the empty pool and the size required to add the object to the pool compared to the length of your array), but I wouldn't obsess over it.



There is absolutely no difference between the first two options. How local storage does not affect ARC - without even relying on the exact details of the ARC implementation, you reassign at the top of the next iteration independently.

(* keep counts are not kept as long as they are greater than 1, since 1 is a very common value and implied by an object existing at all, so they go in a separate table and not with an object)

2018 edit: The 64-bit runtime stores a storage count greater than 1, but less than a very large number in the isa pointer portion, since the entire 64-bit range is not currently required. And if ever, they can just move the save account again. The very large number was 2 ^ 19 + 1 in the original 64-bit runtime, but the small size may have changed since then. Thus, additional storage does not significantly increase the amount of memory on modern devices.

+3


source


The biggest speed improvement is the removal of free calls to valueForKeyPath.

valueForKeyPath is a string argument, such as object.first. Then it has to parse the string: find a point in the middle, find out that the receiver is a dictionary, that the "object" does not start with the @ symbol, so it matches objectForKey, etc. All this three times. Instead



for (NSDictionary *dict in NSArray *arrayOfDicts) {
    SomeObject* someObject = [dict objectForKey:@"object"];

    [array addObject:@[[someObject.first copy], 
                       [someObject.second copy],
                       [someObject.third copy]]];
}

      

It's up to you if you need a copy. Objects often have "copy" properties, so first, secondly, the third can be copies.

+1


source







All Articles