2014-09-11 21 views
9

EDYCJA: Winowajcą był iOS 8, a nie symulator (którego nie zdawałem sobie sprawy już działa system iOS 8). Zmieniono nazwę tego tytułu, aby to odzwierciedlić.Usuwanie awarii grafiki w systemie iOS 8, ale działa na iOS 7

Z przyjemnością użyłem kodu z this SO question, aby załadować okładki albumów z plików mp3. To było na moim iPhone 5 z iOS 7.1.

Ale potem prześledziłem awarię symulatora iOS do tego kodu. Dalsze dochodzenie ujawniło, że ten kod również rozbił się na moim iPadzie. Rozbił się na moim iPadzie po uaktualnieniu do iOS 8.

Wygląda na to, że słownik zawierający obraz jest uszkodzony.

Stworzyłem fikcyjny projekt iOS, który tylko ładuje okładki albumów i uzyskał taki sam wynik. Poniżej znajduje się kod z tego kontrolera viewcontroller.

- (void) viewDidAppear:(BOOL)animated 
{ 
    self.titleText = @"Overkill"; // Set song filename here 
    NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; 
    if (!filePath) { 
     return; 
    } 
    NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 

    NSLog(@"Getting song metadata for %@", self.titleText); 
    AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil]; 
    if (asset != nil) { 
     NSArray *keys = [NSArray arrayWithObjects:@"commonMetadata", nil]; 
     [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ 
      NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata 
                   withKey:AVMetadataCommonKeyArtwork 
                   keySpace:AVMetadataKeySpaceCommon]; 
      UIImage *albumArtWork; 

      for (AVMetadataItem *item in artworks) { 
       if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 
        NSDictionary *dict = [item.value copyWithZone:nil]; 

        // ********** 
        // Crashes here with SIGABRT. dict is not a valid dictionary. 
        // ********** 

        if ([dict objectForKey:@"data"]) { 
         albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; 
        } 
       } 
       else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { 
        // This doesn't appear to get called for images set (ironically) in iTunes 
        albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; 
       } 
      } 

      if (albumArtWork != nil) { 
       dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.albumArtImageView setImage:albumArtWork]; 
       }); 
      } 

     }]; 
    } 

} 

Oznaczyłem linię awarią. Oczekuje, że plik Overkill.mp3 będzie w pakiecie. Testowałem z wieloma plikami mp3 i m4a wyeksportowanymi z iTunes i Amazon, więc wiem, że same pliki są poprawnie zakodowane.

Testowany w Xcode 6.0 i 6.1.

Jakieś pomysły, dlaczego zadziała na iPhonie, ale nie na symulatorze czy iPadzie?

EDIT/UPDATE:

rejestrowania item.value ujawnia różnice.

na iPhone 5 (prace):

(lldb) po item.value 
{ 
    MIME = JPG; 
    data = <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>; 
    identifier = ""; 
    picturetype = Other; 
} 

Na Simulator (awarie)

(lldb) po item.value 
<ffd8ffe0 .... several Kb of data ..... 2a17ffd9> 

Wygląda więc na to, że na symulatorze nie ma słownika, tak surowa grafika.

Zmiana kodu nie oczekiwać słownika, ale wziąć item.value jako UIImage pracuje!

 for (AVMetadataItem *item in artworks) { 
      if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 
       NSData *newImage = [item.value copyWithZone:nil]; 
       albumArtWork = [UIImage imageWithData:newImage]; 
      } 
      ... 
     } 
+0

Ah ... Xcode 6.0. Muszę ci powiedzieć, napisałem ten kod, kiedy jeszcze byłem początkującym. Od tego czasu wiele się zmieniło. Spróbuj zalogować 'item.value',' item' i słownika utworzonego przez skopiowanie 'item.value'. Czy mógłbyś też opublikować sam tekst śledzenia wstecznego i awarii? – duci9y

+0

@ duci9y Dzięki za wskazany kierunek! Zobacz moją aktualizację powyżej. Wydaje się, że item.value to surowe dane UIImage (a nie NSDictionary) podczas korzystania z symulatora lub iPada. Bardzo dziwny. Czy znasz sposób na określenie UIImage vs NSDictionary bez awarii aplikacji? (Patrzę teraz na to.) –

+0

Użyj 'AVMetadataID3MetadataKeyAttachedPicture' dla plików MP3 i' AVMetadataiTunesMetadataKeyCoverArt' dla iTunes zamiast wspólnego klucza i zobacz, czy uzyskasz bardziej przewidywalne wyniki. – duci9y

Odpowiedz

17

Wydaje się, że wrócił struktura dane uległy zmianie w iOS 8. value obiektu AVMetadataItem nie jest słownikiem, ale rzeczywiste dane surowe UIImage.

Dodanie testu dla NSFoundationVersionNumber rozwiązuje problem. Prawdopodobnie istnieje czystsze rozwiązanie.

- (void) viewDidAppear:(BOOL)animated 
{ 
    self.titleText = @"Overkill"; 
    NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; 
    if (!filePath) { 
     return; 
    } 
    NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 

    NSLog(@"Getting song metadata for %@", self.titleText); 
    AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil]; 
    if (asset != nil) { 
     NSArray *keys = [NSArray arrayWithObjects:@"commonMetadata", nil]; 
     [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ 
      NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata 
                   withKey:AVMetadataCommonKeyArtwork 
                   keySpace:AVMetadataKeySpaceCommon]; 
      UIImage *albumArtWork; 

      for (AVMetadataItem *item in artworks) { 
       if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 

        // *** WE TEST THE IOS VERSION HERE *** 

        if (TARGET_OS_IPHONE && NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { 
         NSData *newImage = [item.value copyWithZone:nil]; 
         albumArtWork = [UIImage imageWithData:newImage]; 
        } 
        else { 
         NSDictionary *dict = [item.value copyWithZone:nil]; 
         if ([dict objectForKey:@"data"]) { 
          albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; 
         } 
        } 
       } 
       else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { 
        // This doesn't appear to get called for images set (ironically) in iTunes 
        albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; 
       } 
      } 

      if (albumArtWork != nil) { 
       dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.albumArtImageView setImage:albumArtWork]; 
       }); 
      } 

     }]; 
    } 

} 
+1

Dziękuję bardzo Stan! Czasami mam ochotę wskoczyć do samolotu na SF i dławić kilku pracowników Apple. Dlaczego czują potrzebę marnowania czasu na tego rodzaju nonsensy, których nigdy nie zrozumiem. – amergin

+1

Myślę, że możesz je zrozumieć, gdy opracujesz własny system operacyjny i wydałeś kilka jego wersji. :) –

Powiązane problemy