我正在使用ALAssetsLibrary
将图片保存到照片库。当它进入循环时,它同时运行并导致内存问题。如何在不引起内存问题的情况下运行
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
for(UIImage *image in images){
[library writeImageToSavedPhotosAlbum:[image CGImage]
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:^(NSURL *assetURL, NSError *error)
{
... //
}];
}
答案 0 :(得分:5)
如果要确保这些写入是连续发生的,可以使用信号量等待图像完成,然后再开始下一次写入:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
for (UIImage *image in images) {
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
dispatch_semaphore_signal(semaphore); // signal when done
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing
}
而且,由于您可能不想在此过程中阻止主队列,因此您可能希望将整个事件分配到某个后台队列:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
for (UIImage *image in images) {
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
dispatch_semaphore_signal(semaphore); // signal when done
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing
}
});
或者,您可以将此writeImageToSavedPhotosAlbum
包裹在自定义NSOperation
中,但不会发布isFinished
直到完成块,但这对我来说似乎有点过分了。
话虽如此,我对这个images
数组感到担忧,你在同一时间内将所有UIImage
个对象保存在内存中。如果它们很大,或者你有很多图像,那可能会有问题。通常,您只想维护一组图像名称,然后逐个实例化图像,例如:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
for (NSString *path in imagePaths) {
@autoreleasepool {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
UIImage *image = [UIImage imageWithContentsOfFile:path];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
dispatch_semaphore_signal(semaphore); // signal when done
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing
}
}
});
人们通常应该警惕任何编码模式,这种编码模式假设同时在内存中保存一组大对象,如图像。
如果您需要经常访问这些图像,但并不总是希望每次都从持久存储中重新检索它,您可以使用NSCache
模式(例如尝试从缓存中检索图像;如果不是发现,从持久存储中检索并将其添加到缓存中;在内存压力下,清空缓存),这样您就可以享受在内存中保存图像的性能优势,但可以优雅地处理图像缓存占用过多内存的情况。
答案 1 :(得分:1)
另一种方法是实现“异步循环”:
typedef void(^completion_t)(id result, NSError* error);
- (void) saveImages:(NSMutableArray*)images
toAssetLibrary:(ALAssetsLibrary*)library
completion:(completion_t)completionHandler
{
if ([images count] > 0) {
UIImage* image = [images firstObject];
[images removeObjectAtIndex:0];
[library writeImageToSavedPhotosAlbum:[image CGImage]
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:^(NSURL *assetURL, NSError *error)
{
if (error) {
}
else {
}
[self saveImages:images toAssetLibrary:library completion:completionHandler];
}];
}
else {
// finished
if (completionHandler) {
completionHandler(@"Images saved", nil);
}
}
}
<强>注意:强>
方法saveImages:toAssetLibrary:completion:
是异步方法。
按顺序处理图像列表。
保存所有图像后,将调用完成处理程序。
为了实现这一点,上面的实现在writeImageToSavedPhotosAlbum:orientation:completionBlock:
的完成处理程序中调用它自己。
这不是递归方法调用,但是当完成处理程序调用方法saveImages:toAssetLibrary:completion:
时,方法已经返回。
为简洁起见,样本没有错误处理。这应该在实际实现中得到改进。
最好不要使用图片列表,而是最好使用图片的网址列表。
你可以这样使用它:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[self saveImages:[self.images mutableCopy] toAssetLibrary:library
completion:^(id result, NSError*error) {
...
}];
答案 2 :(得分:0)
如果您希望更新使用dispatch_async。
// This will wait to finish
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});