如何在使用静态方法下载资源的时候 ,让这个资源一直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);
}
}];
}];
}

通过以上方法,你可以确保资源在下载过程中不会被过早回收。根据你的具体需求选择合适的方法。如果还有其他问题,请随时告诉我!