我已经在我自己学习Objective C一段时间了,但仍然没有完全掌握内存管理。我什么时候应该发布属性?
示例,我有一个类将处理2(register& updateParticulars)不同的URLRequest连接。当registerConnection结束时,将执行updateParticularsConnection。
@interface ConnectionViewController : UIViewController {
}
@property (nonatomic, retain) NSURLConnection *registerConnection;
@property (nonatomic, retain) NSURLConnection *updateParticularsConnection;
@property (nonatomic, retain) NSMutableData *responseData;
@property (nonatomic, retain) NSMutableURLRequest *requestURL;
@end
@implementation ConnectionViewController
@synthesize registerConnection, updateParticularsConnection, responseData, requestURL,
(void)performRegistration {
// other here to prepare the data.
requestURL = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myURL"]];
registerConnection = [[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES];
}
(void)updateParticulars {
// other here to prepare the data.
[requestURL setURL:[NSURL URLWithString:@"http:myURL.com"]];
updateParticularsConnection = [[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES];
}
处理委托回调
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[SVProgressHUD dismissWithError:@"Unable to connect"];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (responseData == nil) {
responseData = [[NSMutableData alloc] init];
}
[responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == registerConnection) {
NSMutableString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(@"Register connection recieved data reads : %@", responseString);
if ([responseString isEqualToString:@"-1"]) { // error. stop connection
[self.requestURL release]; // remember to release requestURL since we would not be continuing on.
}
else if ([responseString isEqualToString:@""]) { // error. stop connection
[self.requestURL release]; //remember to release requestURL since we would not be continuing on.
}
else {
[self updateParticulars]; // perform next connection, updateParticulars
}
responseData = nil; // clear the current stored data in preparation for the next connection.
[self.registerConnection release];
[responseString release];
} // end of definition for register connection
else if (connection == updateParticularsConnection) {
// do stuff with data received back here
self.responseData = nil;
[self.requestURL release];
[self.updateParticularsConnection release];
}
}
我的问题是我应该尽快释放我的财产,这是我认为我现在在做什么?或者只在dealloc方法期间?如果我做得不对,请建议。
答案 0 :(得分:2)
你有点需要根据具体情况来看待它。一般的答案是“一旦你完成它”,除非它是一个微不足道的分配。对于简单的分配(例如NSString * firstName
),您可以等到dealloc或它被替换(例如setFirstName:
)。这只是简化了实施。
你的例子略有不同。
// typically, you will set these to nil right when they
// have finished and you have grabbed what you need.
// that's pretty common for an async one shot request.
@property (nonatomic, retain) NSURLConnection *registerConnection;
@property (nonatomic, retain) NSURLConnection *updateParticularsConnection;
@property (nonatomic, retain) NSMutableURLRequest *requestURL;
和
// in the context of your program, this allocation could be large.
// in many cases, you'll simply convert it to the destination (if
// it's an image, just turn it into an image without holding onto
// the data representation), then dispose of it.
@property (nonatomic, retain) NSMutableData *responseData;
重要的是:您正在OP中直接处理您的实例的ivars - 使用访问器,它们将为您节省大量的麻烦。这些是您使用访问器编写程序所做的更改:
- (void)performRegistration {
self.requestURL = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myURL"]] autorelease]; // use setter and autorelease
self.registerConnection = [[[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES] autorelease]; // use setter and autorelease
}
- (void)updateParticulars {
[self.requestURL setURL:[NSURL URLWithString:@"http:myURL.com"]]; // use getter
self.updateParticularsConnection = [[[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES] autorelease]; // use setter and autorelease
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (self.responseData == nil) { // use getter
self.responseData = [NSMutableData data]; // use setter and autorelease
}
[self.responseData appendData:data]; // use getter
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == self.registerConnection) { // use getter
NSMutableString *responseString = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; // use getter
NSLog(@"Register connection recieved data reads : %@", self.responseString); // use getter
if ([responseString isEqualToString:@"-1"]) {
self.requestURL = nil; // use setter
}
else if ([responseString isEqualToString:@""]) {
self.requestURL = nil; // use setter
}
else {
[self updateParticulars];
}
self.responseData = nil; // use setter
self.registerConnection = nil; // use setter
[responseString release];
}
else if (connection == self.updateParticularsConnection) { // use getter
self.responseData = nil; // use setter
self.requestURL = nil; // use setter
self.updateParticularsConnection = nil; // use setter
}
}
答案 1 :(得分:2)
我仅将@property
用于可以从此类外部获取/设置的公共类变量。
对于您拥有的requestURL
等私有变量,无需创建retain
ed属性。
对于定义中声明为retain
的每个变量,设置self.variable
会将保留计数增加1。必须记住这一点,因为最常见的泄漏问题是将属性设置为已保留的值,如self.myString = [[NSString alloc] init]
。在这里,即使您没有预期,myString
也会保留2个。
那么你的何时发布?
的问题适用于班级dealloc方法中的@property
: 和私有变量:完成后使用。
答案 2 :(得分:1)
您通常应该尝试将alloc / retain和release保持在同一范围内。如果值是属性,则它的范围对于对象实例是全局的,并且应该在dealloc方法中释放。如果值是方法中的局部变量(并且随后未分配给更全局范围的变量),那么显然您必须在该方法中释放它,并且您应该尝试在相同的{}括号内执行释放你在哪里做了alloc / retain。
例如,如果您发现在代码中的某个点之后不再需要属性,那么此时可能会将其取消,但是将该版本保留在dealloc方法中仍然是明智的。
对于具有委托的对象,它偶尔会在委托方法中释放对象,但是您应该始终在alloc位置记录它,并且应该100%确定通过委托的所有“最终”路径发布。
这些不是硬/快规则,但它们可以帮助您摆脱困境。在修改程序的过程中,改变控制流程或其他一些并且导致错过“巧妙放置”的版本,这简直太容易了。通过观察范围规则,您很少遇到这样的问题。 (并记录范围规则的任何例外情况。)
答案 3 :(得分:0)
release
上的备忘单:
如果您用于创建对象的方法包含单词
new
,copy
或alloc
,则您赢了,并且必须释放一次你不再需要参考了。
答案 4 :(得分:0)
你是对的。
您可以在需要时(以及如果)创建它们,并在您不再需要它们时立即释放它们,这样您将节省内存。 为init中的所有属性和iVars分配内存可以减慢实例化过程。 查找对象的内存是Cocoa中最慢的任务之一,你必须尝试在CPU使用率和内存消耗之间取得完美的平衡 - 你不必担心这个级别的CPU使用率。
在Cocoa中如果向nil对象发送消息没有任何反应,那么如果你确定为每个副本/ alloc / retain调用了release,那么你应该将它设置为nil。
在iOS 5.0中你可以使用ARC完全不需要自己为cocoa做内存管理,你仍然需要为corefoundation和其他基于C的API创建/保留/发布。