委托和对象保留objective-c (iOS)

Posted

技术标签:

【中文标题】委托和对象保留objective-c (iOS)【英文标题】:Delegates and object retention objective-c (iOS) 【发布时间】:2013-07-14 04:58:55 【问题描述】:

我在保留数组中的对象时遇到问题。我正在构建一个框架来与 Amazon Route 53 交互,并尽可能地关注 AWS ios 开发工具包的其余部分。我正在调用返回 XML 的 Web 服务。来自 Web 服务的响应由 NSXMLParser 委托编组到一个类中。委托切换到另一个委托来处理 XML 的嵌套部分。第二个委托类将嵌套部分解组为第二个类。第二类的对象放在第一类的一个数组中。当第二个委托被释放时,它正在释放对象(不是它们所在的数组,只是对象本身。当响应返回时,所有属性都被填充,但数组中的对象指向释放的对象(以及当然最终会调用一个已释放的对象)

这涉及到很多代码,所以我希望从委托中将对象分配给数组有明显的意义。我很确定我在整个过程中都在维护强引用,并且我认为如果将对象添加到数组中,则在释放数组之前它们不能/不会从中释放。

如果您有兴趣查看代码,请点击此处:

https://bitbucket.org/entr04y/aws-sdk-ios

应用程序位于示例子目录 (Route53_demo) 中,初始调用位于 ListHostedZonesViewController.m (viewWillAppear) 中。这会调用 src 目录中的 Route53 框架 - AmazonRoute53Client.m,它调用 Route53ListHostedZonesRequestMarshaller.m 作为解组响应的委托。第二个代表是 Route53HostedZoneUnmarshaller.m。

希望有人能指出我遗漏了什么......过去四天我一直在盯着它看,但我无法弄清楚我所做的与某些示例代码不同遵循相同模式的 AWS。

按照下面的要求,我将尝试添加代码的相关部分,因为这样做的方式似乎并不明显:

初始请求是从这样的视图控制器发出的:

Route53ListHostedZonesRequest *request = [[Route53ListHostedZonesRequest alloc] initWithMaxItems:100];

Route53ListHostedZonesResponse *listHostedZonesResponse = [[AmazonClientManager r53] listHostedZones:request];

然后在 AmazonRoute53Client.m(在我的框架中)中处理:

-(Route53ListHostedZonesResponse *)listHostedZones:(Route53ListHostedZonesRequest *)listHostedZonesRequest


AmazonServiceRequest *request = [Route53ListHostedZonesRequestMarshaller createRequest:listHostedZonesRequest];

return (Route53ListHostedZonesResponse *)[self invoke:request rawRequest:listHostedZonesRequest unmarshallerDelegate:[Route53ListHostedZonesResponseUnmarshaller class]];

响应被发送到 Route53ListHostedZonesResponseUnmarshaller.m 并解析为 Route53ListHostedZonesResponse 类的实例:

 -(Route53ListHostedZonesResponse *)response
 
 if (nil == response) 
    response = [[Route53ListHostedZonesResponse alloc] init];

return response;

当解析器命中包含托管区域的嵌套部分时,它被委派给另一个解组器 Route53HostedZoneUnmarshaller:

if ([elementName isEqualToString:@"HostedZone"]) 
    [parser setDelegate:[[[Route53HostedZoneUnmarshaller alloc] initWithCaller:self withParentObject:self.response.hostedZones withSetter:@selector(addObject:)] autorelease]];

(hostedZones 被声明为 NSMutableArray 并且是 Route53HostedZonesResponse 类的非原子保留属性),它将 xml 中的项目放置到 XML 中包含的每个托管区域的 Route53HostedZone 实例中:

-(Route53HostedZone *)response

if (nil == response) 
    response = [[Route53HostedZone alloc] init];

return response;

在 xml 块的末尾,对象被返回给调用者,如下所示:

if ([elementName isEqualToString:@"HostedZone"])  
    if ( caller != nil) 
        [parser setDelegate:caller];
    
   if (parentObject != nil && [parentObject respondsToSelector:parentSetter]) 
        [parentObject performSelector:parentSetter withObject:self.response];
     
    return;


在 xml 响应结束时,Route53ListHostedZonesResponse 对象填充如下:

if ([elementName isEqualToString:@"ListHostedZonesResponse"])

    if (caller != nil) 
        [parser setDelegate:caller];
    

    if (parentObject != nil && [parentObject respondsToSelector:parentSetter]) 
        [parentObject performSelector:parentSetter withObject:self.response];
    

    return;

此时,AWS 运行时库和 Route53 库已完成对响应的处理,解组器对象、Route53ListHostedZonesResponse 对象和所有 Route53HostedZone 对象都已释放。

回到视图控制器,当我尝试访问 listHostedZoneResponse 中的对象时,所有属性都可用,除了 hostsZones 数组中的对象现在是僵尸。我在 Route53HostedZoneUnmarshaller.m 中覆盖了 dealloc,如下所示:

-(void)dealloc

AMZLogDebug(@"deallocating zone object unmarshaller");
//   [response release];
//    [super dealloc];

这会阻止 unmarshaller 被释放并通过扩展 hostsZones 对象,此时代码可以工作(但当然会像筛子一样泄漏)我怎样才能将 Route53HostedZone 对象放入 hostsZones 数组以便保留它们?

【问题讨论】:

与其对正在发生的事情进行干巴巴的解释,为什么不放一些简化的代码来说明你在做什么呢?复制+粘贴嵌套的委托对象,删除除数组存储部分和委托处理代码之外的所有内容。这将真正提高获得解决方案的机会。 您是否对所有这些代码运行了静态分析器?你有收到任何警告吗?你修好了吗? 我同意@Can。由于您没有提供任何示例代码,因此我不了解您所面临问题的具体情况。对于没有处理过它的人来说,挖掘 AWS iOS 开发工具包是没有效率的。 @robmayoff:我得到的唯一警告是我已经覆盖了 dealloc。 @Can,希望这足以让您了解我在说什么。我最初希望是方法,而不是实现问题。 【参考方案1】:

哇,这是一些令人费解的代码。我只是猜测问题可能出在这一行:

[parser setDelegate:
 [[[Route53HostedZoneUnmarshaller alloc]
                               initWithCaller:self 
                             withParentObject:self.response.hostedZones 
                                   withSetter:@selector(addObject:)] 
  autorelease]
 ];

我看不出parser 的类型是什么,但在Objective-C 中,通常保留代表。所以整个Route53HostedZoneUnmarshaller在使用的时候可能就是垃圾了。

尝试删除对autorelease 的调用并将委托对象存储在属性中,直到解析过程完成。

【讨论】:

解析器是一个 NSXMLParser。正如预期的那样,在解析过程完成之前,委托类将一直保留。问题是它创建的对象也在解析过程结束时被释放,即使它们被添加到最初在应用程序中创建的响应对象的 hostsZones 数组中。我认为将它们添加到数组中会保留它们。我想弄清楚的是如何将hostedZone对象添加到hostedZones数组中,以使它们以与原始响应对象中的所有其他对象相同的方式保留。 NSMutableArray 中的对象被保留。当对象还在数组中时,它不可能变成僵尸。是什么让你觉得你得到了僵尸?您是否启用了 NSZombieEnabled 标志?您的对象可能会在它们添加到数组之前被释放。或者您正在访问数组中某个对象的属性并且该属性已被释放(数组无法控制这些属性)。 我在应用中启用了 NSZombie,我只是检查了一下,并没有在框架中打开它。一旦我这样做了,我发现被释放对象的原始问题不再是问题(实际上昨天已经修复),但是我输入的调试代码发现它现在是问题 - 谢谢!

以上是关于委托和对象保留objective-c (iOS)的主要内容,如果未能解决你的问题,请参考以下文章

Objective-c (iOS) 中常见的 HTTP 请求委托模式

自定义委托方法触发正常,但值不会保留在其他方法中

在 Swift 上委托 Objective-C 协议

使用 ARC 的 Objective-C 代表

Objective-C 运行时委托问题

Objective-C 确定哪些对象保留另一个对象