自定义 NSDictionary getter 的正确、内存管理实现
预先感谢...
所以在最近的一些内存泄漏和错误之后(记录这里),我一直在研究我的内存管理,现在我正在尝试在我拥有的辅助类中为 NSDictionary 编写一个自定义 getter。
自定义 getter 的原因是:
首先,它是一个计算字典,因此对于性能问题,我想返回缓存的对象,除非有必要重新生成它(我下面的示例在计算上并不太昂贵,但类中的其他示例将是)。
其次,我想延迟实例化。我只想在另一个类“请求”时生成字典,这就是我在 getter 中检查 nil 的原因。
那么,问题。下面的代码代表了我第一次(研究过)编写自定义 setter/getter 的尝试,我通常只是综合。
1)这是正确的实现和正确的内存管理吗?
2)这是最好的吗?
3) BOOL installedStandardsChangedSinceLastRead
是标记需要重新计算的最佳方法吗?如果类中的其他方法更改了某些内容,就会发生这种情况。但我是否应该让这些方法 nil out _installedStandards
来代替,因为这也会触发重新计算?
4)最后,如果我没有只是让其他方法将 iVar 清零以将其标记为重新计算,我如何确保不会泄漏?我会使用setter(即self.installedStandards = nil
),直接nil iVar(即_installedStandards = nil
),还是其他东西?
(哦,如果我直接取消 iVar,我需要先释放它吗?似乎正确的是 [_installedStandards release]; _installedStandards = nil
?在这种情况下我不能只需使用self.installedStandards = nil
?)
进入代码! 提醒一下,这是一个更复杂的类的简化版本(请参阅顶部的链接),其中包含许多这些 setter/getter。我需要确保在充实它之前正确执行。
.h 文件
@interface InstalledStandardTracker20 : NSObject {
NSDictionary *_installedStandards;
BOOL _installedStandardsChangedSinceLastRead;
}
@property (nonatomic, retain) NSDictionary *installedStandards;
@property (nonatomic) BOOL installedStandardsChangedSinceLastRead;
@end
@implementation
@implementation InstalledStandardTracker20
@synthesize installedStandardsChangedSinceLastRead = _installedStandardsChangedSinceLastRead;
- (void)refreshInstalledStandards {
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
self.installedStandards = [currentDefaults objectForKey:@"installedStandards"];
self.installedStandardsChangedSinceLastRead = NO;
}
- (NSDictionary *)installedStandards {
if (!_installedStandards || self.installedStandardsChangedSinceLastRead) {
[self refreshInstalledStandards];
}
return _installedStandards;
}
- (void)setInstalledStandards:(NSDictionary *)newInstalledStandards {
[newInstalledStandards retain];
[_installedStandards release];
_installedStandards = newInstalledStandards;
}
Thanks in advance...
So after some recent memory leaks and bugs (documented here), I've been studying up on my memory management, and I am now trying to write a custom getter for an NSDictionary in a helper class I have.
The reasons for the custom getter are:
First, it's a calculated dictionary, so for performance issues I want to return the cached object unless it's necessary to regenerate it (my example below isn't too computationally expensive, but others in the class will be).
Second, I want to lazily instantiate. I only want to generate the dictionary if another class "asks" for it, which is why I'm checking for nil in the getter.
So, the questions. The code below represents my first ever (studied) attempt at writing custom setter/getter, I usually just synthesize.
1) Is this proper implementation and proper memory management?
2) Is this best?
3) Is the BOOL installedStandardsChangedSinceLastRead
the best way to flag the need to recalculate? This would happen if other methods in the class had changed something. But should I just have those methods nil out _installedStandards
instead, since that would also trigger the recalculate?
4) Finally, if I did just have the other methods nil out the iVar to flag it for recalculation, how would I make sure I don't leak? Would I use the setter (i.e. self.installedStandards = nil
), nil the iVar directly (i.e. _installedStandards = nil
), or something else?
(Oh, and if I nil'd the iVar directly, would I need to release it first? It seems like correct would be [_installedStandards release]; _installedStandards = nil
? In which case couldn't I just use self.installedStandards = nil
?)
Onto the code!
Reminder that this is a simplified version of what will be a more complex class (see link at top) with lots of these setters/getters. I need to make sure I'm executing properly before I flesh it out.
.h file
@interface InstalledStandardTracker20 : NSObject {
NSDictionary *_installedStandards;
BOOL _installedStandardsChangedSinceLastRead;
}
@property (nonatomic, retain) NSDictionary *installedStandards;
@property (nonatomic) BOOL installedStandardsChangedSinceLastRead;
@end
@implementation
@implementation InstalledStandardTracker20
@synthesize installedStandardsChangedSinceLastRead = _installedStandardsChangedSinceLastRead;
- (void)refreshInstalledStandards {
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
self.installedStandards = [currentDefaults objectForKey:@"installedStandards"];
self.installedStandardsChangedSinceLastRead = NO;
}
- (NSDictionary *)installedStandards {
if (!_installedStandards || self.installedStandardsChangedSinceLastRead) {
[self refreshInstalledStandards];
}
return _installedStandards;
}
- (void)setInstalledStandards:(NSDictionary *)newInstalledStandards {
[newInstalledStandards retain];
[_installedStandards release];
_installedStandards = newInstalledStandards;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这个问题很难回答,因为课堂之外没有背景。该代码似乎没有内存泄漏。另外,您不需要实施 -setInstalledStandards:。由于您在保留属性上调用 @synthesize,生成的 setter 看起来就像这样。本质上,您正在使用完全相同的方法重写该方法。
对于您的另一个问题,清空 iVar 与内存中保存的对象无关。一旦释放 iVar,指针中包含的内存地址处的对象就不再保证存在。通过清空 iVar,您只需将该内存地址重置为指向 nil。这可以避免您收到 EXC_BAD_ACCESS 运行时异常,因为程序的其他部分不会尝试访问这个现在未定义的地址空间。
从本质上讲,你总是必须释放它。虽然将其置空是一种很好的做法,但只要您在知道该指针在内存中包含有效对象之前不尝试访问该指针,就不需要这样做。我希望这是有道理的。
This question is rather difficult to answer because there is no context outside of the class. The code appears to be memory-leak free. Also, you don't need to implement -setInstalledStandards:. Since you call @synthesize on a retain property, the generated setter would look just like that. In essence, you're overriding the method with exactly the same method.
For your other question, nilling out an iVar has nothing to do with what objects are being held in memory. As soon as you release the iVar, the object at the memory address contained in your pointer is no longer guaranteed to exist. By nilling the iVar, you are simply resetting that memory address to point to nil. This avoids you getting an EXC_BAD_ACCESS runtime exception because no other parts of your program will attempt to access this now undefined address space.
In essence, you always have to release it. While nilling it is good practice, it is not required so long as you don't try to access that pointer until you know it contains a valid object in memory. I hope that makes sense.