2012-08-27 79 views
1

更新:這裏也報道此問題,原因的更詳細的治療:的Objective-C:UIImageWriteToSavedPhotosAlbum()+異步=問題

UIImageWriteToSavedPhotosAlbum saves only 5 image out of 10. Why?

在我的情況下,該錯誤是:「寫繁​​忙」 - 這似乎是與設備速度有關的問題。可能有一些解決方案需要手動處理線程或類似的東西 - 但是,在Tommy的回答下面,我序列化了保存圖像,並且解決了這個問題。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

現狀:

我試圖使用for-loop(圖像數量取決於用戶輸入 - 可能是1,2,3或理論上數百)保存圖像的大集合到相機卷。 NSSet指向圖像(儘管我可以很容易地執行NSArray),並且在for循環完成後NSSet設置爲nil

我使用UIImageWriteToSavedPhotosAlbum()要保存在分離的線程的圖像(使用NSThreaddetachNewThreadSelector:toTarget:withObject:類方法),並啓動主線程上UIActivityIndicator微調。

問題:

當我試圖挽救了超過〜5,〜5日將輸出此錯誤日誌中之後的任何圖像更:

*** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL

例如,讓我們說我導出了9張圖片。 for循環將遍歷所有9個圖像(由NSLogging確認),但我會繞過上述錯誤的4次迭代(並且只有5個圖像保存到相機膠捲中)。

但是,如果我在循環中添加一個斷點並在每次迭代之間等待一兩秒鐘,它們全部都會正確保存而不會有任何抱怨。所以..

理論:

根據我的記錄和觀察,UIImageWriteToSavedPhotosAlbum()顯然是異步運行,在某種程度上「太慢」跟上我的應用程序。

有沒有一種簡單的方法來強制它同步運行(理想的主線程)?我一直在試圖爲我想要保存的圖像添加引用計數,但是(1)這感覺很拙劣,並且(2)我沒有解決問題。

任何建議將是偉大的。謝謝!

+0

鎖定主線程意味着你的活動指示燈不會動畫。你的應用程序會掛起。 – danielbeard

+1

是的,'UIImageWriteToSavedPhotosAlbum'以異步方式運行。錯誤日誌聽起來像是要保存的圖像爲空,請檢查圖像數據。 – fannheyward

+0

@danielbeard - 是的,正確的,對不起 - 在**情況的第二段中澄清了我的線程** – toblerpwn

回答

3

如果你不想一次寫多個圖像,那麼你可以使用UIImageWriteToSavedPhotosAlbum的完成目標和選擇器來實現尾遞歸的一種形式。

- (void)writeSetToSavedPhotosAlbum:(NSMutableSet *)images 
{ 
    if(![images count]) return; 

    UIImage *imageToSave = [[[images anyObject] retain] autorelease]; 
    [images removeObject:imageToSave]; 
    NSLog(@"I shall now write image %@", imageToSave); 

    UIImageWriteToSavedPhotosAlbum(
      imageToSave, 
      self, 
      @selector(writeSetToSavedPhotosAlbum:), 
      images); 
} 

編輯:喜歡的東西,也可能是值得看你是否得到相同的結果與ALAssetsLibrary-writeImageToSavedPhotosAlbum:orientation:completionBlock:,這需要一個塊完成這樣就更簡單的工作。

+0

我會試驗這一點,但只是爲了確認:**我百分之百寫好所有一次**的圖像。你的回答暗示可能有辦法做到這一點?這個循環只是我的實現,用於將所有圖像傳遞到'UIImageWriteToSavedPhotosAlbum()'.. – toblerpwn

+0

對不起,我的意思是不要一次寫多個圖像,即使它不希望「UIImageWriteToSavedPhotosAlbum」平行工作。所以尾遞歸答案我給部隊一個串行轉換和寫無論。所以如果Apple的代碼中存在某種問題,這將是一種解決方法。我不知道任何方法將一組或一系列圖像直接傳遞給資產庫,並要求它根據其優先級調度來編寫所有這些方法。 – Tommy

+0

我已經使用類似於您的方法來解決問題或多或少(我將用細節更新問題)。它現在對我的活動指示器造成嚴重破壞,因爲在寫出實際圖像之前主線程已經「完成」,但是現在我們正在解決一個單獨的問題,我可以解決這個問題。:) – toblerpwn

0

文檔確實提到你會被異步通知。我想知道這是否可行:對於要保存的每個圖像創建一個塊操作並將其添加到主操作隊列中。有點像:

for (UIImage *toSave in imagesToSave) 
{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     UIImageWriteToSavedPhotosAlbum(toSave, target, selector, ctx); 
    }]; 
} 

可能值得一試,讓我知道它是否有幫助!

+0

好的想法,卡爾,但我的企圖沒有骰子(在幾個地方測試過).. – toblerpwn

+0

是的,回想起來這可能不是一個好主意 - 但也許實現一些併發的NSOperation子類可以很好地完成這項任務。 –

+0

很難說 - 似乎蘋果的方法(或者它在這種情況下是一種功能?)只是有自己的想法,不幸的是,心靈只能一次處理幾個請求:)在我的應用程序的下一個版本,我將試驗這一點(並行運行串行設置 - 如果可行,請嘗試3 - 如果可行,請嘗試4等),但我最好在幾周後離開從那:)在同一時間,串行,一次一個時間節省將不得不做.. – toblerpwn

6

我用ALAssetsLibrary和dispatch_queue,以確保圖像保存順序:

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 
dispatch_queue_t queue = dispatch_queue_create("com.myApp.saveToCameraRoll", NULL); 

[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) { 

    dispatch_async(queue, ^{ 

     dispatch_semaphore_t sema = dispatch_semaphore_create(0); 

     [library writeImageToSavedPhotosAlbum:image.CGImage metadata:metaData completionBlock:^(NSURL *assetURL, NSError *writeError) { 

      if (writeError) { 
       // handle the error 
      } 
      else { 
       if (image == [images lastObject]) { 
        dispatch_async(dispatch_get_main_queue(), ^{ 
         // perhaps indicate to the user that save has finished 
        }); 
       } 
      } 

      dispatch_semaphore_signal(sema); 

     }]; 

     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
     dispatch_release(sema); 
    }); 
}]; 
0

在此代碼的工作保存圖像:

UIImageWriteToSavedPhotosAlbum([self screenshot], nil, nil, nil); 

UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Save" message:@"Photo saved to album" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
[alert show];