便捷方法与 init 方法的内存使用情况
最近,当我研究 iPhone 内存管理时,我尝试比较同一对象上的便捷方法和 init 方法。 例如,我有 UIImageView,它显示下载的 NSData:
便捷方法:
imageView.image = [UIImage imageWithData:[downloads dataAtIndex:0]];
init 方法:
UIImage *aImage = [[UIImage alloc] initWithData:[downloads dataAtIndex:0]];
imageView.image = aImage;
[aImage release];
当我尝试在视图上来回移动以增加内存使用量并点击“模拟内存警告”时,应用程序的内存使用量增加了使用便捷方法从 20MB 到 18MB,init 方法立即从 20MB 到 13MB。
我还等待并与应用程序交互,以便有时间在便捷方法的自动释放上进行释放。 但并没有下降太多。
除了自动释放与发布之外,还有什么造成了差异?
Recently when I looked into iPhone memory management, I tried to compare the convenience method and init method on the same object. For example, I have UIImageView where it displays a downloaded NSData:
Convenience method:
imageView.image = [UIImage imageWithData:[downloads dataAtIndex:0]];
init method:
UIImage *aImage = [[UIImage alloc] initWithData:[downloads dataAtIndex:0]];
imageView.image = aImage;
[aImage release];
When I try to go back and forth on the views to increase the memory usage and hit "Simulate Memory Warning", the memory usage for the app went from 20MB to 18MB with convenience method, and init method went from 20MB to 13MB immediately.
I also waited and interacted with the app to give time on releasing on the convenience method's autorelease. But it didn't drop much.
Other than the autorelease vs release, what else contributed the difference?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
通过便捷方法创建的自动释放对象在包含的自动释放池耗尽之前不会被释放。 如果您将大量使用内存密集型操作,建议将内存密集型操作包装在 NSAutoreleasePool 块内。
Your autoreleased objects created by the convenience methods will not be released until the containing autorelease pool is drained. It is advisable to wrap memory intensive operations inside of an
NSAutoreleasePool
block if you will be making heavy use of them.可能导致此问题的唯一区别是 imageWithData: 不使用系统图像缓存,而 initWithData: 则使用。 因此,也许使用初始化程序创建的图像在收到内存警告时可以释放其图像数据,因为它可以返回到系统缓存,而由便捷方法创建的图像则不能。
The only difference that could be causing this is that imageWithData: doesn't use the system image cache, while initWithData: does. So maybe the image that's made with the initializer can release it's image data when it receives a memory warning since it can go back to the system cache, while the one that's created by the convenience method can't.
我相信您知道,使用便捷方法创建的对象是自动释放的。 但是,如果您在创建映像的 RunLoop 中没有 AutoReleasePool,则该对象将被添加到不存在的池中,并且永远不会被正确清理。 如果您正在线程中运行,并且忘记为该线程创建 AutoReleasePool,则可能会出现这种情况。
要验证情况是否如此,您可以运行 Instruments (Leaks) 并查看其报告的内容。
Objects created with convenience methods are autoreleased, as I am sure you are aware. However, if you have no AutoReleasePool in the RunLoop where you are creating the image, then the object will be added to a non-existent pool, and never properly cleaned up. This may be the case if you are running in a thread, and have forgotten to create an AutoReleasePool for that thread.
To verify if this is the case, you could run Instruments (Leaks) and see what it reports.
我注意到,对于自动释放的对象,在内存压力下,如果不及时释放池,则会发生系统 GC,并导致明显的性能下降。
此外,使用 init/release 方法可以让循环中的内存消耗保持平稳,而使用自动释放池会产生峰值。 在某些内存不足的情况下,自动释放池中对象的快速增加可能会导致您的应用程序在系统采取措施清理诸如打开的电子邮件、移动 Safari 选项卡和运行的 iTunes 等内容之前启动应用程序。
最重要的是,我倾向于更多地使用 init,因为它使我的应用程序的内存消耗更加一致,并且我发现随机启动的问题更少。 内存消耗的逐渐增加使守护进程可以清理系统而不会杀死我的应用程序。 最后,顺便说一句,如果您在类中使用 @property 关键字,则必须小心以下内容:
因为会发生的情况是,当 main.m 类中的池被收集时,该项目将消失,由于调用已释放对象上的方法而导致崩溃。 如果您使用@property(非原子,保留)设置它,您将需要使用:
以确保它挂起。 不过,您可以通过使用 alloc init 来避免所有这些。 只需观察引用计数,以确保没有双重引用导致内存泄漏。
Something that I have noticed is that with autoreleased objects, under memory pressure, there is a system GC that occurs with a noticeable performance hit if you don't release your pools in a timely fashion.
Also, using the init / release method allows your memory consumption in loops to remain flat, while using a autorelease pool creates spikes. In some memory challenged contitions, the rapid increase of objects in the autorelease pool may cause your application to get booted before the system takes steps to clean up stuff like emails being open, mobile safari tabs, and iTunes stuff running.
Bottom line, I tend to use init more because it makes my application's memory consumption more consistent and I see fewer problems with getting booted randomly. The gradual increase in memory consumption lets the daemons clean up the system without killing my app. Finally, as somewhat of an aside, if you are using the @property keyword in your classes, you have to be careful with stuff like:
Because what will happen is when the pool in your main.m class gets collected that item will be gone, causing a crash due to calling a method on a released object. If you are setting it with @property (nonatomic, retain) you will want to use:
to make sure it hangs around. You can avoid all of that by just going with alloc init however. Just watch your reference counts to make sure you don't have double references causing memory leaks.