【问题标题】:Variable released prematurely during asynchronous request在异步请求期间过早释放的变量
【发布时间】:2012-04-27 09:14:15
【问题描述】:

我的内存管理和线程知识非常有限,所以我可能遗漏了一些非常基本的东西。我已经找到了解决这个问题的方法,但它真的让我很困扰,因为我看不到正在发生的事情。

我有一个类发出两个异步 HTTP 请求,一个用于 XML 配置文件,另一个用于图像。因为在同一个类中有两个异步请求,所以我重用了相同的NSURLConnectionDelegate 方法(可能是因素)。我首先异步获取配置文件并提取两个 url,分别存储为 sponsorImagesponsorUrl。然后我使用sponsorImage 的值来异步获取图像数据。不过,我发现,在获得图像后(在第二个异步事件完成后),sponsorUrl 已被释放。

我偶然发现,如果我在创建图像请求的方法中“对它做某事”,我可以阻止 sponsorUrl 被释放——我所说的“做某事”就是这样。基本上,我的代码如下所示:

- (void) loadImage
{
    sponsorUrl = [sponsorUrl stringByAppendingString:@""];

    NSURL *url = [NSURL URLWithString:sponsorImage];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setValue:dateString forHTTPHeaderField:@"If-Modified-Since"];

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
    [connection release];   
}

如果我删除此方法的第一行(我在 url 中添加“”),则保留变量。如果我删除它,它就会被释放。这对我来说毫无意义。

我尝试用@propterty (nonatomic, retain) 声明sponsorUrl;我试过静态声明sponsorUrl;我试过在我设置它的地方添加[sponsorUrl retain],但这并没有什么不同。唯一有效的是在发出第二个请求之前“触摸”变量。

我错过了什么?

【问题讨论】:

  • 你试过 [alloc init];在赞助商网址上。我有一个类似的问题,这解决了它......试试吧

标签: iphone objective-c memory-management


【解决方案1】:

当您使用便利构造函数时,变量会自动释放!仅当您使用 alloc、copy 或 new 等方法时,它们才会被隐式保留。

其次,通过编写赞助商Url = ...。您使用的不是生成的setter,而是生成的实例变量。你需要写 self.sponsorUrl = @"Blah";或 [self setSponsorUrl:@"blah"] 以便让 setter 保留变量。

【讨论】:

  • 你和@sergio 是对的。我知道 @"" 是糖,但我没有意识到其中的含义。所以如果你用[[NSString alloc] initWithString:@""]初始化字符串,那么重置字符串的最好方法是什么?你只是再次[sponsorUrl initWithString:@""]
  • “重置”是什么意思?由于普通字符串通常是不可变的,因此字符串的值在他的整个生命周期中都是相同的。如果要清除值,只需传递一个空字符串的新实例,如 self.sponsorUrl = [NSString initWithString:@""];甚至更好,只需将其设置为 nil。
  • 对不起。那是一个愚蠢的问题。我只是不习惯在这么低的水平上思考。如果你有这个:NSString *mystring = @"one"; mystring = @"two";... 在这里,@"one" 将被释放。由于@"" 的糖分,@"two" 将在稍后自动释放。 ...对吗?
  • 不,像 NSString *myString = @"blah" 这样创建的字符串永远不会被释放。在您的评论中,您正在创建 NSConstantString 的实例。看看我一年前问过的问题:stackoverflow.com/questions/4804943/…
【解决方案2】:

确实,您的内存管理似乎有些问题。

很难解释发生了什么,因为您没有提供使用变量的完整代码。举个例子,取语句:

sponsorUrl = [sponsorUrl stringByAppendingString:@""];

您所做的是为sponsorURL 分配一个新值;旧值(您首先初始化变量的那个,即获得您提到的retain 的那个)被释放(stringByAppendingString 伪造一个新对象); sponsorURL 指向的新对象是一个自动释放的对象,它的生命周期并不完全清楚:我们只知道它会在某个时候被释放(可能在下一次主循环迭代中)。因此,通过“触摸”变量,您正在为其分配一个新值,该值的生命周期从您触摸变量的点开始......无论如何都是非常不可靠的。

我的建议如下:

  1. 在你的类中定义两个属性来处理sponsorURLsponsorImage

  2. 将它们设为retain 类型;

  3. 仅通过它们的访问器方法为它们赋值,即 self.sponsorURL = [...]

  4. 确保您分配给属性的任何对象都是 autoreleased 对象(否则,请使用分配进行释放)。

如果您提供更多代码,则可以更彻底地审查它,但如果您遵循上述指南,您将完全没有问题(大约...)

【讨论】:

  • 您和@yan.kun 正确识别了问题。对不起,我没有包含更多我的代码。没有很多,但我认为包括整个事情会掩盖问题。两个问题:在我的代码中,sponsorUrl 实际上是静态的;制作静态的东西会改变它的保留方式吗?并连接一个我想保留的字符串,我应该做[[someString stringByAppendingString:@"..."] retain]
  • static 在文件(全局)级别将使变量可以从该文件中以独占方式访问;它只是一个词法的东西,与生命周期或保留周期无关。关于第二个问题,你是对的:stringByAppendingString 会给你一个 autorelease 对象;如果你想让它保持活动状态(在你的控制之下),你需要调用retain 或者通过属性设置器(self.sponsorURL =...)分配它,它会为你做保留。
猜你喜欢
  • 2017-04-18
  • 2016-09-09
  • 2016-05-04
  • 2012-09-04
  • 2022-11-02
  • 1970-01-01
  • 2012-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多