如何从ARC中的id __autoreleasing *转换为void **?

时间:2012-08-13 17:57:05

标签: objective-c automatic-ref-counting ocmock

我正在尝试从id __autoreleasing *转换为CFTypeRef *(void **)。

我试过了:

id __autoreleasing *arg = [OCMArg setTo:mockData];

CFTypeRef expectedResult = (__bridge CFTypeRef) *arg;

[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck
                                                                                          result:&expectedResult];

但是当自动释放池耗尽时,代码崩溃了。

如何在ARC环境中转换为void **?

2 个答案:

答案 0 :(得分:3)

我不知道你正在使用的API,所以我不能100%确定发生了什么。我用谷歌搜索,他们似乎是OCMock的一部分。我下载了它(没有安装它,因为我不感兴趣)我迅速浏览了源代码。

我在那段代码中看到了一些非常可疑的东西。以下是他们如何实现您调用的第一种方法:

@implementation OCMArg

....

+ (id *)setTo:(id)value
{
    return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease];
}

所以他们返回id*,这实际上只是id

对我来说,这是一个无意义/错误或试图操纵ObjC内部(即使没有记录,ObjC对象存储的第一件事实际上是指向对象类的指针,因此类型为Classid兼容,因此以某种方式将指向对象的引用或引用对象的id转换为Class*id*)是有效的。我没有时间或兴趣去研究整个API以找出他们为什么这样做。它们实际上可能有一个很好的理由(例如,如果你只将结果传递给另一个知道它应该是什么的API,但你在这里做的不仅仅是这个)。而不是研究OCMock,我会试着向你解释我发生的事情(ObjC和ARC)。

id __autoreleasing *arg = [OCMArg setTo:mockData];

ARC在这行代码中绝对不会做任何事情。

您可以在上面看到这种方法。类OCMPassByRefSetter是一个简单的类,只保留参数后保留它,因此保留mockDataOCMPassByRefSetter已自动释放,并会在下一次排空时消失(释放mockData 使*arg引用已释放的内存。)

请注意,arg实际上指向isa的{​​{1}}(OCMPassByRefSetter是任何对象的“第一个”ivar,它的类型为{{1}并指向对象的类。但这是没有记录的,可能随时改变)。

isa

Class属于CFTypeRef expectedResult = (__bridge CFTypeRef) *arg; 类型,与*arg兼容,因此广告投放有效。你使用id所以ARC绝对没有。

如果CFTypeRef指向“免费桥接”CF / Cocoa类,这将是完全有效的代码,但您必须小心,__bridge在下一次排放时将变为无效(它是不是arg,而是作为自动释放的实例生效。

expectedResult

不知道这条线的作用。鉴于您在上面的评论中发布的原型,ARC在retained部分没有做任何事情。

你说它是[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck result:&expectedResult]; 的包装,但正如我所理解的那样,它不止于此。如果它只是立即调用result:&expectedResult传递SecItemCopyMatching参数,那么你可能会搞砸了。但名称SecItemCopyMatching以及这是OCMock的事实让我觉得这比这更复杂。

你必须自己调查一下。但请记住:

  • 当前函数退出时,您传递的参数(result:)将变为无效,因为它是一个局部变量。
  • 一旦排空,expectedResult将变为无效,因为该地址指向将由排水管解除分配的内存。
  • 使用值&expectedResult执行任何可能会出错,因为我不认为expectedResult符合“免费桥接”的条件。

我怀疑,但我可能非常错误,你没有按照预期的方式使用OCMock apis。但是在这方面我无法帮助你,也许你实际上做得对。

答案 1 :(得分:0)

我没有尝试弄清楚如何将变量转换为正确的格式(OCMock在内部做了一些复杂的事情),而是添加了另一种方法来处理转换。

- (OSStatus)findItemMatching:(NSDictionary *)query result:(id __autoreleasing *)outResult {
    NSAssert(outResult, @"outResult is required");

    CFTypeRef result = nil;
    OSStatus status = [self copyItemMatching:query result:&result];

    if (result) {
        *outResult = CFBridgingRelease(result);
    }

    return status;
}