HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Optimizing a search for list of artists in iTunes library

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
itunessearchartistsoptimizingforlibrarylist

Problem

I'm trying to get a list of all artists on an iPhone / iPod touch music library. The best way I've found has been to group by artists (the convenience constructor on MPMediaQuery) and to iterate over the collections, being one for each artist. That way I've optimized out having to use an NSSet to ensure uniqueness in the artist list. Still, I need to make each collection into a representative item and get it's artist, which according to Time Profiler takes a very good part of this code snippet.

NSString       *artistKey    = [MPMediaItem titlePropertyForGroupingType:MPMediaGroupingArtist];
MPMediaQuery   *artistsQuery = [MPMediaQuery artistsQuery];
NSMutableArray *artists      = [NSMutableArray arrayWithCapacity:artistsQuery.collections.count];

for (MPMediaItemCollection *album in artistsQuery.collections) {
    MPMediaItem *albumItem = [album representativeItem];
    [artists addObject:[albumItem valueForProperty:artistKey]];
}


Wrapping it in some timing blocks, such as below, I've gotten it to run in 0.315 seconds on my iPhone 4 with a library of ~2600 songs and 88 artists, but I feel like that long for only 88 items is way too long.

NSTimeInterval start  = [[NSDate date] timeIntervalSince1970];
// [snip]
NSTimeInterval finish = [[NSDate date] timeIntervalSince1970];
NSLog(@"Execution took %f seconds.", finish - start]);


Does anyone know of a way to make this a bit faster?

Solution

Is it actually necessary to add just the artist to the array? We could simply do this:

NSMutableArray *artists = artistsQuery.collections;


Now we don't have to iterate through an entire array. And later, when we need to access the artist at a particular array index, we can still get to that value as such:

[[artists[someIndex] representativeItem] valueForProperty:artistKey];


Also, this SO answer seems to suggest that valueForProperty: is just slow in general. Despite only needing the artist, it might be better to enumerate through all the properties:

[artistsQuery.collections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    MPMediaItem *item = (MPMediaItem *)obj;
    [item enumerateValuesForProperties:properties usingBlock:^(NSString *property, id value, BOOL *stop) {
        if ([property isEqualToString:artistKey]) {
            [artists addObject:value];
        }
    }];
}];


At first glance it may seem like this will automatically be slower since you're iterating through every property instead of just accessing the one you need, and in this case, since you only need a single property, this may actually be slower. But if you need multiple properties, this should definitely be faster--enumerations like this (as well as forin loops) are handled in batches so they run quite fast.

Code Snippets

NSMutableArray *artists = artistsQuery.collections;
[[artists[someIndex] representativeItem] valueForProperty:artistKey];
[artistsQuery.collections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    MPMediaItem *item = (MPMediaItem *)obj;
    [item enumerateValuesForProperties:properties usingBlock:^(NSString *property, id value, BOOL *stop) {
        if ([property isEqualToString:artistKey]) {
            [artists addObject:value];
        }
    }];
}];

Context

StackExchange Code Review Q#4605, answer score: 3

Revisions (0)

No revisions yet.