Posts 处理上传图片太多导致内存崩溃的问题
Post
Cancel

处理上传图片太多导致内存崩溃的问题

问题分析

批量上传图片,当图片多了的时候,内存崩溃了。

Message from debugger: Terminated due to memory issue

解决方法

创建队列,用信号量,实现图片一张一张上传。

1
2
3
4
5
static SSTUploadHomeworkPictureManager *manager = nil;
static dispatch_queue_t _queueUploadBegin = nil; // 创建串行队列 - 保证多次图片上传请求按顺序执行;
static dispatch_semaphore_t _semaphoreBegin = nil;
static dispatch_queue_t _queueSubmitBegin = nil;
static dispatch_semaphore_t _semaphoreSubmitBegin = nil;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+ (instancetype)shareManager{
    return [[self alloc] init];
}

- (instancetype)init{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [super init];
        _queueUploadBegin = dispatch_queue_create("com.uploadHomeworkImageToOSS.subsystem.tast", DISPATCH_QUEUE_SERIAL);
        _semaphoreBegin = dispatch_semaphore_create(1); // 设置信号总量为1,保证只有一个进程执行
        _queueSubmitBegin = dispatch_queue_create("com.submitHomeworkImageToOSS.subsystem.tast", DISPATCH_QUEUE_SERIAL);
        _semaphoreSubmitBegin = dispatch_semaphore_create(1);
    });
    return manager;
}
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
26
27
-(void)submitHomewrokPhoto:(NSArray<SSTEditTaskSujectFinishedPictureModel *> *)imageModels withVipId:(NSString *)vipId subject:(NSString*)subject {
    self.imageCount = imageModels.count;
    dispatch_async(_queueSubmitBegin, ^{
        dispatch_semaphore_wait(_semaphoreSubmitBegin, DISPATCH_TIME_FOREVER);
        NSMutableArray *uploadPictures = [NSMutableArray array];
        dispatch_queue_t queueUploadImage = dispatch_queue_create("com.submitImageModel.subsystem.tast", DISPATCH_QUEUE_SERIAL);
        dispatch_semaphore_t semaphoreImage = dispatch_semaphore_create(1); // 设置信号总量为1,保证只有一个进程执行;
        for (NSInteger i = 0; i < imageModels.count; i ++) {
            SSTEditTaskSujectFinishedPictureModel *editPicture = [imageModels objectAtIndexCheck:i];
            dispatch_async(queueUploadImage, ^{
                dispatch_semaphore_wait(semaphoreImage, DISPATCH_TIME_FOREVER);
                editPicture.correctStatus = SSTHomeworkPictureStateUploading;
                editPicture.isLocalData = YES;
                editPicture.vipId = vipId;
                editPicture.course = subject;
                [self saveUploadingHomewrokPhoto:editPicture withVipId:vipId subject:subject];
                [uploadPictures addObject:editPicture];
                dispatch_semaphore_signal(semaphoreImage);
                if (i == self.imageCount - 1) {
                    [self uploadImageModelArr:uploadPictures originalPhoto:YES complete:^(SSTEditTaskSujectFinishedPictureModel * _Nonnull imageModel, AliOSSUploadImageState state) {                        
                        dispatch_semaphore_signal(_semaphoreSubmitBegin);
                    }];
                }
            });
        }
    });
}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
- (void)uploadImageModelArr:(NSArray<SSTEditTaskSujectFinishedPictureModel *> *)imageModelArr originalPhoto:(BOOL)originalPhoto complete:(void(^)(SSTEditTaskSujectFinishedPictureModel *imageModel, AliOSSUploadImageState state))complete {
    NSLog(@" =========== uploadImageModelArr ========= start--%@",[NSThread currentThread]);
    dispatch_async(_queueUploadBegin, ^{
        //等待信号量
        dispatch_semaphore_wait(_semaphoreBegin, DISPATCH_TIME_FOREVER);
        //网络请求上传图片数据
        [XWHttpClient xw_postWithUrlString:HTTP_upload_getOssfsToken Params:nil DefaultHUD:NO SuccessBlock:^(id returnValue) {
            NSLog(@"  ----- getOssfsToken - returnValue: %@",returnValue);
            NSLog(@" =========== HTTP_upload_getOssfsToken ========= start--%@",[NSThread currentThread]);
            OssfsTokenModel *ossfsTokenModel = [OssfsTokenModel mj_objectWithKeyValues:DicGetValue(returnValue,@"data")];
            id<OSSCredentialProvider> credential = [[OSSStsTokenCredentialProvider alloc] initWithAccessKeyId:ossfsTokenModel.accessKeyId secretKeyId:ossfsTokenModel.accessKeySecret securityToken:ossfsTokenModel.securityToken];
            OSSClient *client = [[OSSClient alloc] initWithEndpoint:ossfsTokenModel.endpoint credentialProvider:credential];
            OSSPutObjectRequest *put = [OSSPutObjectRequest new];
            put.bucketName = ossfsTokenModel.bucket;
            NSOperationQueue *queue = [[NSOperationQueue alloc] init];
            queue.maxConcurrentOperationCount = imageModelArr.count;

            //创建串行队列
            dispatch_queue_t queueUpload = dispatch_queue_create("com.uploadLocalPicture.subsystem.tast", DISPATCH_QUEUE_SERIAL);
            //设置信号总量为1,保证只有一个进程执行
            dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

            for (int i = 0; i < imageModelArr.count; i ++) {
                SSTEditTaskSujectFinishedPictureModel *imageModel = imageModelArr[i];
                imageModel.index = i;
                dispatch_async(queueUpload, ^{
                    //等待信号量
                    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

                    //处理耗时操作的代码块...
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                        UIImage *image = imageModel.localImage;
                        if (image) { // image
                            NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                            NSData *data;
                            NSData *data1 = UIImageJPEGRepresentation(image, 0.5);
                            float length1 = [data1 length] / 1024;
                            if (length1 < 400) {
                                data = data1; //UIImageJPEGRepresentation(image, 1);
                            }else {
                                if (originalPhoto) {
                                    data = UIImageJPEGRepresentation(image, 0.5); // 原图
                                }else {
                                    data = UIImageJPEGRepresentation(image, 0.3); // 再压缩一下
                                }
                            }
                            NSString *imageName = [NSString stringWithFormat:@"/iOS/%@%d.jpg",[SSUtility getTransactionID],i];  //加“i”防止数组图片重名
                            //imageModel.localPath = [NSString stringWithFormat:@"%@%d.jpg",[SSUtility getTransactionID],i]; // 用于本地保存,如果上传失败的话;
                            put.objectKey = [imageName substringFromIndex:1];
                            NSTimeInterval endTime = [[NSDate date] timeIntervalSince1970]*1000;// *1000 是精确到毫秒,不乘就是精确到秒
                            NSInteger uploadTime = endTime;
                            imageModel.derverFileId = imageModel.derverFileId.length?imageModel.derverFileId:[NSString stringWithFormat:@"%@-%zd",imageModel.vipId,uploadTime];
                            put.uploadingData = data;
                            put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
                             NSLog(@" -- 这是第 %d 张图片 %@ 在上传--- bytesSent %lld, - totalByteSent %lld, - totalBytesExpectedToSend %lld", i,imageName,bytesSent, totalByteSent, totalBytesExpectedToSend);
                            };
                            NSString *paramBody = @"{\"vipId\":${x:vipId},\"courseName\":${x:courseName},\"derverFileId\":${x:derverFileId},\"fileKeys\":${x:fileKeys},\"questionRecommendRecordId\":${x:questionRecommendRecordId}}";
                            put.callbackParam = @{@"callbackUrl":[NSString stringWithFormat:@"%@%@?accessToken=%@",kUPLOAD_ADDRESS,HTTP_End_Path(HTTP_LibraryTeacher,@"uploadPicture"),[SSUtility getTeacherToken]],
                                                 @"callbackBody":paramBody,
                                             @"callbackBodyType":@"application/json"
                                                }; // library/V2
                            put.callbackVar = @{@"x:vipId":imageModel.vipId,
                                           @"x:courseName":imageModel.course,
                                             @"x:fileKeys":imageName,
                                         @"x:derverFileId":imageModel.derverFileId,
                            @"x:questionRecommendRecordId":imageModel.questionRecommendRecordId.length?imageModel.questionRecommendRecordId:@""};
                            OSSTask *putTask = [client putObject:put];
                            [putTask waitUntilFinished]; // 阻塞直到上传完成
                            [putTask continueWithBlock:^id(OSSTask *task) {
                                OSSPutObjectResult *result = (OSSPutObjectResult *)task.result;
                                NSLog(@"Result - requestId: %@,\n headerFields: %@,\n servercallback: %@",
                                            result.requestId,
                                            result.httpResponseHeaderFields,
                                            result.serverReturnJsonString);
                                NSDictionary *dataDict = [SSUtility dictionaryWithJsonString:result.serverReturnJsonString];
                                NSString *codeStr = dataDict[@"code"];
                                NSString *msgStr = dataDict[@"msg"];

                                // 还要从后台返回数据判断是否上服务器成功
                                if ([codeStr isEqualToString:@"999"]) {
                                    NSLog(@"upload object success!");
                                    // 删除上传中
                                    [[SSTUploadHomeworkPictureManager shareManager] deleteUploadingPhoto:imageModel withVipId:imageModel.vipId subject:imageModel.course];
                                    if (complete) {
                                        complete(imageModel ,AliOSSUploadImageSuccess);
                                    }
                                }else {
                                    NSLog(@"请求服务器失败:%@",msgStr.length?msgStr:task.error);
                                    dispatch_async(dispatch_get_main_queue(), ^{
                                        [[SSCustomAlertView alloc] setAlertViewTitle:@"" andMessage:msgStr ? msgStr : @"作业上传失败" andhideAlertViewTimeOut:kDefaultAlertTime];
                                    });
                                    imageModel.correctStatus = SSTHomeworkPictureStateUploadFail;
                                    [[SSTUploadHomeworkPictureManager shareManager] saveUploadFailurePhoto:imageModel withVipId:imageModel.vipId subject:imageModel.course];
                                    [[SSTUploadHomeworkPictureManager shareManager] deleteUploadingPhoto:imageModel withVipId:imageModel.vipId subject:imageModel.course];
                                    if (complete) {
                                        complete(imageModel ,AliOSSUploadImageFailed);
                                    }
                                }

                                dispatch_semaphore_signal(semaphore);
                                //最后一张上传完了,这个请求结束了;
                                if (imageModel == imageModelArr.lastObject) { //要是按顺序来的才能这么判断;
                                    NSLog(@" --- 网络请求上传图片数据 --- upload object finished!");
                                    dispatch_semaphore_signal(_semaphoreBegin);
                                }

                                task = [client presignPublicURLWithBucketName:ossfsTokenModel.bucket withObjectKey:put.objectKey];
                                return nil;
                            }];

                            }];

                            if (queue.operations.count != 0) {
                                [operation addDependency:queue.operations.lastObject];
                            }
                            [queue addOperation:operation];

                        }else { //没有图片,让线程继续执行;当做是失败;
                            imageModel.correctStatus = SSTHomeworkPictureStateUploadFail;
                            [[SSTUploadHomeworkPictureManager shareManager] saveUploadFailurePhoto:imageModel withVipId:imageModel.vipId subject:imageModel.course];
                            [[SSTUploadHomeworkPictureManager shareManager] deleteUploadingPhoto:imageModel withVipId:imageModel.vipId subject:imageModel.course];
                            dispatch_semaphore_signal(semaphore);
                            if (complete) {
                                 complete(imageModel ,AliOSSUploadImageFailed);
                            }
                        }

                    }); // 子线程操作

                }); // 等待信号量

            } // 遍历图片

            [queue waitUntilAllOperationsAreFinished];

        } ErrorBlock:^(id errorCode) {
            dispatch_semaphore_signal(_semaphoreBegin);
            for (SSTEditTaskSujectFinishedPictureModel *pictureImageModel in imageModelArr) { // 上传中的作业置为上传失败
                pictureImageModel.correctStatus = SSTHomeworkPictureStateUploadFail;
                [[SSTUploadHomeworkPictureManager shareManager] saveUploadFailurePhoto:pictureImageModel withVipId:pictureImageModel.vipId subject:pictureImageModel.course];
                [[SSTUploadHomeworkPictureManager shareManager] deleteUploadingPhoto:pictureImageModel withVipId:pictureImageModel.vipId subject:pictureImageModel.course];
                if (complete) {
                    complete(pictureImageModel ,AliOSSUploadImageFailed);
                }
            }
        } FailureBlock:^(id failureInfo) {
            dispatch_semaphore_signal(_semaphoreBegin);
            for (SSTEditTaskSujectFinishedPictureModel *pictureImageModel in imageModelArr) { // 上传中的作业置为上传失败
                pictureImageModel.correctStatus = SSTHomeworkPictureStateUploadFail;
                [[SSTUploadHomeworkPictureManager shareManager] saveUploadFailurePhoto:pictureImageModel withVipId:pictureImageModel.vipId subject:pictureImageModel.course];
                [[SSTUploadHomeworkPictureManager shareManager] deleteUploadingPhoto:pictureImageModel withVipId:pictureImageModel.vipId subject:pictureImageModel.course];
                if (complete) {
                    complete(pictureImageModel ,AliOSSUploadImageFailed);
                }
            }
        }];
    }); // 等待信号量

}

错误示范

这种方法开辟子线程太多了,十分消耗内存,也会造成内存崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSMutableArray *uploadPictures = [NSMutableArray array];
        dispatch_group_t group = dispatch_group_create();
        for (SSTEditTaskSujectFinishedPictureModel *editPicture in imageModels) {
            dispatch_group_async(group, queue, ^{
                editPicture.correctStatus = SSTHomeworkPictureStateUploading;
                editPicture.isLocalData = YES;
                editPicture.vipId = vipId;
                editPicture.course = subject;
                NSLog(@" -- 上传 保存 作业 图片 当前线程 -- %@",[NSThread currentThread]);
                [self saveUploadingHomewrokPhoto:editPicture withVipId:vipId subject:subject];
                [uploadPictures addObject:editPicture];
            });
        }
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_group_notify(group, queue, ^{
            if (uploadPictures.count > 0) {
                [self uploadImageModelArr:uploadPictures originalPhoto:YES complete:^(SSTEditTaskSujectFinishedPictureModel * _Nonnull imageModel, AliOSSUploadImageState state) {
                    SSLog(@" --- 作业上传 --- 完成 --- ");
                }];
            }
        });
    });

其他方法

作业图片1

作业图片2

作业图片3

作业图片4

This post is licensed under CC BY 4.0 by the author.

多线程 - 重复网络请求优化

本地先下载PDF文件,再用WKWebView显示

Comments powered by Disqus.