7
\$\begingroup\$

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?

\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ You'd think they'd optimize valueForProperty: rather than document it's slowness. I'm not working on this code anymore but I'm accepting the answer since it sounds right. :) \$\endgroup\$
    – wjl
    Jul 7 '14 at 2:01
  • \$\begingroup\$ Yeah, I agree. Certainly seems odd. valueForProperty: seems significantly less clunky to use, so it's too bad it appears to be documented as inefficient. \$\endgroup\$
    – nhgrif
    Jul 7 '14 at 2:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.