更快的MPMediaItems的NSMutableArray吗?代码审查 [英] Faster sort of NSMutableArray of MPMediaItems? Code Review
问题描述
我已经进行了几周的iOS编程,还有很多东西要学习.我有一个包含MPMediaItems的NSMutableArray,但与1200个项目相比,它慢了大约10秒钟,我正在寻找一种更快的方法.
我的最终目标是拥有一组MPMediaItemCollection项目,每个项目代表一张专辑.就我所知,我无法从MPMediaQuery中获取此内容,因为我需要从播放列表中获取歌曲.因此,我对从特定播放列表(过去4个月")中获得的歌曲进行排序,然后构建自己的收藏集.正如我所说,下面的方法有效,但是速度很慢.即使我仅按MPMediaItemPropertyAlbumTitle排序,它仍然需要大约4秒钟(iPhone 4S).
我应该提到我尝试了排序描述符,但是我无法获得工作的钥匙.例如
NSSortDescriptor *titleDescriptor = [[NSSortDescriptor alloc] initWithKey:@"MPMediaItemPropertyAlbumTitle" ascending:YES];
这将返回错误
[<MPConcreteMediaItem 0x155e50> valueForUndefinedKey:]: this class is not key value coding-compliant for the key MPMediaItemPropertyAlbumTitle.
代码
MPMediaQuery *query = [MPMediaQuery playlistsQuery];
NSArray *playlists = [query collections];
NSMutableArray *songArray = [[NSMutableArray alloc] init];
for (MPMediaItemCollection *playlist in playlists) {
NSString *playlistName = [playlist valueForProperty: MPMediaPlaylistPropertyName];
NSLog (@"%@", playlistName);
if ([playlistName isEqualToString:@"Last 4 months"]) {
/* replaced this code with a mutable copy
NSArray *songs = [playlist items];
for (MPMediaItem *song in songs) {
[songArray addObject:song];
}
*/
// the following replaces the above for-loop
songArray = [[playlist items] mutableCopy ];
[songArray sortUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first1 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *second1 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *first2 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *second2 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *first3 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *second3 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *first = [NSString stringWithFormat:@"%@%@%03d",first1,first2, [first3 intValue]];
NSString *second = [NSString stringWithFormat:@"%@%@%03d",second1,second2, [second3 intValue]];
return [first compare:second];
}];
}
}
在@cdelacroix的建议下,我重新实现了我的比较块,以层叠三个排序键,如果高阶键相同,则仅检查低阶键.这样可以减少50%以上的执行时间.尽管如此,它并没有我想要的快,所以如果有人有更好的答案,请发布它.
这里是旧时代对新时代(1221件):
4S执行时间= 10.8,执行时间= 4.7
3GS执行时间= 21.6,执行时间= 9.3
有趣的是,从for循环到数组副本的mutableCopy切换似乎并没有改善.如果有的话,mutableCopy可能慢了十分之一秒(有人在mutableCopy上进行了基准测试吗?).但是我保留了更改,因为它看上去更干净.最后,请注意检查专辑标题== nil.请注意,compare认为具有nil值的任何事物始终与其他事物一样为NSOrderedSame.列表中的一张专辑没有标题集,如果不进行检查,这会弄乱排序顺序.
MPMediaQuery *query = [MPMediaQuery playlistsQuery];
NSArray *playlists = [query collections];
NSMutableArray *songArray = [[NSMutableArray alloc] init];
for (MPMediaItemCollection *playlist in playlists) {
NSString *playlistName = [playlist valueForProperty: MPMediaPlaylistPropertyName];
NSLog (@"%@", playlistName);
if ([playlistName isEqualToString:@"Last 4 months"]) {
songArray = [[playlist items] mutableCopy ];
[songArray sortUsingComparator:^NSComparisonResult(id a, id b) {
NSComparisonResult compareResult;
NSString *first1 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTitle];
if(first1 == nil) first1 = @" "; // critical because compare will match nil to anything and result in NSOrderedSame
NSString *second1 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTitle];
if(second1 == nil) second1 = @" "; // critical because compare will match nil to anything and result in NSOrderedSame
compareResult = [first1 compare:second1];
if (compareResult == NSOrderedSame) {
NSString *first2 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *second2 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumPersistentID];
compareResult = [first2 compare:second2];
if(compareResult == NSOrderedSame) {
NSString *first3 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *second3 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
compareResult = [first3 compare:second3];
}
}
return compareResult;
}];
}
}
I'm a couple weeks into iOS programming, and have lots to learn. I've got a sort of an NSMutableArray containing MPMediaItems working, but it's about 10 seconds slow with a sort of 1200 items and I'm looking for an approach that would be faster.
My ultimate goal is to have an array of MPMediaItemCollection items, each representing an album. I can't get this from an MPMediaQuery (as far as I know) because I need to get the songs from a playlist. So I'm sorting the songs I get from a specific playlist ("Last 4 months") and will then build my own array of collections. As I say, the approach below works but is very slow. Even if I sort only by the MPMediaItemPropertyAlbumTitle, it still takes about 4 seconds (iPhone 4S).
EDIT: I should mention that I tried sort descriptors, but I can't get the key to work. E.g.
NSSortDescriptor *titleDescriptor = [[NSSortDescriptor alloc] initWithKey:@"MPMediaItemPropertyAlbumTitle" ascending:YES];
This return an error of
[<MPConcreteMediaItem 0x155e50> valueForUndefinedKey:]: this class is not key value coding-compliant for the key MPMediaItemPropertyAlbumTitle.
The Code
MPMediaQuery *query = [MPMediaQuery playlistsQuery];
NSArray *playlists = [query collections];
NSMutableArray *songArray = [[NSMutableArray alloc] init];
for (MPMediaItemCollection *playlist in playlists) {
NSString *playlistName = [playlist valueForProperty: MPMediaPlaylistPropertyName];
NSLog (@"%@", playlistName);
if ([playlistName isEqualToString:@"Last 4 months"]) {
/* replaced this code with a mutable copy
NSArray *songs = [playlist items];
for (MPMediaItem *song in songs) {
[songArray addObject:song];
}
*/
// the following replaces the above for-loop
songArray = [[playlist items] mutableCopy ];
[songArray sortUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first1 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *second1 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *first2 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *second2 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *first3 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *second3 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *first = [NSString stringWithFormat:@"%@%@%03d",first1,first2, [first3 intValue]];
NSString *second = [NSString stringWithFormat:@"%@%@%03d",second1,second2, [second3 intValue]];
return [first compare:second];
}];
}
}
With the suggestion of @cdelacroix I reimplemented my comparison block to cascade the three sort keys, only checking lower order keys if the higher order keys were the same. This resulted in a more than 50% reduction in execution time of the sort. Still, it's not as fast as I would like, so if anyone has a better answer, please post it.
Here are old times vs. new times (1221 items):
4S executionTime = 10.8, executionTime = 4.7
3GS executionTime = 21.6, executionTime = 9.3
Interestingly, the switch from a for-loop to a mutableCopy for the array copy did not seem to improve things. If anything, mutableCopy was perhaps a 10th of a second slower (anyone done any benchmarking on mutableCopy?). But I left the change in because it is so much cleaner looking.
Lastly, note the check for album title == nil. Be aware that compare thinks anything with a nil value is always NSOrderedSame as anything else. One album in the list didn't have a title set, and this screwed up the sort order without this check.
MPMediaQuery *query = [MPMediaQuery playlistsQuery];
NSArray *playlists = [query collections];
NSMutableArray *songArray = [[NSMutableArray alloc] init];
for (MPMediaItemCollection *playlist in playlists) {
NSString *playlistName = [playlist valueForProperty: MPMediaPlaylistPropertyName];
NSLog (@"%@", playlistName);
if ([playlistName isEqualToString:@"Last 4 months"]) {
songArray = [[playlist items] mutableCopy ];
[songArray sortUsingComparator:^NSComparisonResult(id a, id b) {
NSComparisonResult compareResult;
NSString *first1 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTitle];
if(first1 == nil) first1 = @" "; // critical because compare will match nil to anything and result in NSOrderedSame
NSString *second1 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTitle];
if(second1 == nil) second1 = @" "; // critical because compare will match nil to anything and result in NSOrderedSame
compareResult = [first1 compare:second1];
if (compareResult == NSOrderedSame) {
NSString *first2 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumPersistentID];
NSString *second2 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumPersistentID];
compareResult = [first2 compare:second2];
if(compareResult == NSOrderedSame) {
NSString *first3 = [(MPMediaItem*)a valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
NSString *second3 = [(MPMediaItem*)b valueForProperty:MPMediaItemPropertyAlbumTrackNumber];
compareResult = [first3 compare:second3];
}
}
return compareResult;
}];
}
}
这篇关于更快的MPMediaItems的NSMutableArray吗?代码审查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!