Objective C 使用 @autorelease 和 ARC 的 For 循环
Objective C For loops with @autorelease and ARC
作为允许审计员创建调查结果并将照片关联到他们的应用程序的一部分(由于网络服务的限制,保存为 Base64 字符串)我必须在审计中循环遍历所有调查结果及其照片并设置他们的同步值为真。
当我执行此循环时,我看到内存峰值从大约 40MB 跳跃到 500MB(对于大约 350 张照片和 255 个发现)并且这个数字永远不会下降。在尝试使用此功能之前,我们的用户平均会创建大约 1000 个调查结果和 500-700 张照片。我试图使用@autorelease pools 来减少内存,但它似乎永远不会被释放。
for (Finding * __autoreleasing f in self.audit.findings){
@autoreleasepool {
[f setToSync:@YES];
NSLog(@"%@", f.idFinding);
for (FindingPhoto * __autoreleasing p in f.photos){
@autoreleasepool {
[p setToSync:@YES];
p = nil;
}
}
f = nil;
}
}
关系和保留周期如下所示
审计对调查有很强的参考意义
Finding 对 Audit 的引用很弱,对 FindingPhoto 的引用很强
FindingPhoto 对 Finding 的引用较弱
就能够有效地循环遍历这些对象并设置它们的属性而不会导致如此巨大的内存峰值而言,我错过了什么。我假设它与循环时加载到内存中但从未被释放的这么多 Base64 字符串有关。
因此,首先,请确保您在提取请求上设置了批量大小。选择一个相对较小的数字,但不要太小,因为这不是用于 UI 处理。您希望将合理数量的对象批处理到内存中以减少加载开销,同时降低内存使用量。尝试 50 或 100,看看效果如何,然后考虑稍微增加批量大小。
如果您正在加载的所有对象都是托管对象,那么在处理过程中驱逐它们的正确方法是将它们变成故障。这是通过在上下文中调用 refreshObject:mergeChanges:
来完成的。但是-丢弃任何更改,并且您的循环专门用于进行更改。
所以,你真正应该做的是批量保存你修改过的对象,然后将这些对象恢复为错误,以从内存中删除数据。
因此,在您的循环中,记录您已修改的数量,并在每次达到该计数时保存上下文并刷新到目前为止已处理的所有对象。提取的批次和要保存的批次大小应该是相同的数字。
您的 "Finding" 对象和相关图像之间的大小可能有很大差异。因此,您的主要目标应该是以某种方式重新设计您的数据库,以便无故障(加载)Finding
对象不会自动加载 base64 编码图像。
这实际上是代码数据的主要优势之一:加载对象层次结构的一部分。只需尝试将 base64 编码数据移动到自己的(托管)对象,以便 Core Data 不会加载它。当引用被触及时,它仍然会根据需要加载。
作为允许审计员创建调查结果并将照片关联到他们的应用程序的一部分(由于网络服务的限制,保存为 Base64 字符串)我必须在审计中循环遍历所有调查结果及其照片并设置他们的同步值为真。
当我执行此循环时,我看到内存峰值从大约 40MB 跳跃到 500MB(对于大约 350 张照片和 255 个发现)并且这个数字永远不会下降。在尝试使用此功能之前,我们的用户平均会创建大约 1000 个调查结果和 500-700 张照片。我试图使用@autorelease pools 来减少内存,但它似乎永远不会被释放。
for (Finding * __autoreleasing f in self.audit.findings){
@autoreleasepool {
[f setToSync:@YES];
NSLog(@"%@", f.idFinding);
for (FindingPhoto * __autoreleasing p in f.photos){
@autoreleasepool {
[p setToSync:@YES];
p = nil;
}
}
f = nil;
}
}
关系和保留周期如下所示
审计对调查有很强的参考意义
Finding 对 Audit 的引用很弱,对 FindingPhoto 的引用很强
FindingPhoto 对 Finding 的引用较弱
就能够有效地循环遍历这些对象并设置它们的属性而不会导致如此巨大的内存峰值而言,我错过了什么。我假设它与循环时加载到内存中但从未被释放的这么多 Base64 字符串有关。
因此,首先,请确保您在提取请求上设置了批量大小。选择一个相对较小的数字,但不要太小,因为这不是用于 UI 处理。您希望将合理数量的对象批处理到内存中以减少加载开销,同时降低内存使用量。尝试 50 或 100,看看效果如何,然后考虑稍微增加批量大小。
如果您正在加载的所有对象都是托管对象,那么在处理过程中驱逐它们的正确方法是将它们变成故障。这是通过在上下文中调用 refreshObject:mergeChanges:
来完成的。但是-丢弃任何更改,并且您的循环专门用于进行更改。
所以,你真正应该做的是批量保存你修改过的对象,然后将这些对象恢复为错误,以从内存中删除数据。
因此,在您的循环中,记录您已修改的数量,并在每次达到该计数时保存上下文并刷新到目前为止已处理的所有对象。提取的批次和要保存的批次大小应该是相同的数字。
您的 "Finding" 对象和相关图像之间的大小可能有很大差异。因此,您的主要目标应该是以某种方式重新设计您的数据库,以便无故障(加载)Finding
对象不会自动加载 base64 编码图像。
这实际上是代码数据的主要优势之一:加载对象层次结构的一部分。只需尝试将 base64 编码数据移动到自己的(托管)对象,以便 Core Data 不会加载它。当引用被触及时,它仍然会根据需要加载。