如何在使用静态方法下载资源的时候 ,让这个资源一直hold住不被回收?
错误示例如下,下面这份代码,会在异步回调的时候,出现tempView已经被释放的情况,出现garbage pointer。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| + (AnyPromise *)downloadFileWithURL:(NSURL *)url type:(WCFinderCoverImageType)type { return [AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolve) { UIView *tempView = [[UIView alloc] init]; weakify(tempView); [tempView wcfSetImageWithURL:url completion:^(UIImage *image, NSString *key, NSError *error, WCFParam param) { strongify_or_return(tempView); if (error) { resolve(error); } else { [image attachObject:@(type) forKey:kFinderCoverImageTypeKey]; resolve(image); } tempView = nil; }]; }]; }
|
在你提供的代码中,tempView
是一个临时视图,用于持有下载的资源(如图片)。为了确保资源在下载完成后不被过早回收,你需要确保 tempView
在下载完成之前不会被释放。以下是一些改进建议:
1. 确保 tempView
的生命周期
在当前的实现中,tempView
在下载完成后被置为 nil
,这可能会导致资源被过早回收。你可以通过以下方式确保 tempView
的生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| + (AnyPromise *)downloadFileWithURL:(NSURL *)url type:(WCFinderCoverImageType)type { return [AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolve) { UIView *tempView = [[UIView alloc] init]; weakify(tempView); [tempView wcfSetImageWithURL:url completion:^(UIImage *image, NSString *key, NSError *error, WCFParam param) { strongify_or_return(tempView); if (error) { resolve(error); } else { [image attachObject:@(type) forKey:kFinderCoverImageTypeKey]; resolve(image); } // 不要在这里将 tempView 置为 nil }]; // 将 tempView 的生命周期延长到 promise 完成 [self holdView:tempView untilPromiseCompletion:resolve]; }]; }
+ (void)holdView:(UIView *)view untilPromiseCompletion:(PMKResolver)resolve { // 这里可以添加逻辑来确保 view 在 promise 完成之前不会被释放 // 例如,可以将 view 添加到一个全局的容器中,直到 promise 完成后再移除 }
|
2. 使用全局容器
你可以使用一个全局的容器来持有 tempView
,直到下载完成后再释放它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static NSMutableArray *holdingViews;
+ (AnyPromise *)downloadFileWithURL:(NSURL *)url type:(WCFinderCoverImageType)type { return [AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolve) { if (!holdingViews) { holdingViews = [NSMutableArray array]; } UIView *tempView = [[UIView alloc] init]; [holdingViews addObject:tempView]; weakify(tempView); [tempView wcfSetImageWithURL:url completion:^(UIImage *image, NSString *key, NSError *error, WCFParam param) { strongify_or_return(tempView); if (error) { resolve(error); } else { [image attachObject:@(type) forKey:kFinderCoverImageTypeKey]; resolve(image); } [holdingViews removeObject:tempView]; }]; }]; }
|
3. 使用 NSURLSession
直接下载
如果你不需要使用 UIView
来持有资源,可以直接使用 NSURLSession
来下载资源,这样可以避免视图的生命周期问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| + (AnyPromise *)downloadFileWithURL:(NSURL *)url type:(WCFinderCoverImageType)type { return [AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolve) { NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { resolve(error); } else { UIImage *image = [UIImage imageWithData:data]; [image attachObject:@(type) forKey:kFinderCoverImageTypeKey]; resolve(image); } }]; [dataTask resume]; }]; }
|
4. 使用 SDWebImage
或其他第三方库
如果你使用的是 SDWebImage
或其他类似的第三方库,它们通常会处理资源的生命周期问题。你可以直接使用这些库来下载和缓存图片:
1 2 3 4 5 6 7 8 9 10 11 12
| + (AnyPromise *)downloadFileWithURL:(NSURL *)url type:(WCFinderCoverImageType)type { return [AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolve) { [[SDWebImageManager sharedManager] loadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (error) { resolve(error); } else { [image attachObject:@(type) forKey:kFinderCoverImageTypeKey]; resolve(image); } }]; }]; }
|
通过以上方法,你可以确保资源在下载过程中不会被过早回收。根据你的具体需求选择合适的方法。如果还有其他问题,请随时告诉我!