我正在尝试从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 **?
答案 0 :(得分:3)
我不知道你正在使用的API,所以我不能100%确定发生了什么。我用谷歌搜索,他们似乎是OCMock的一部分。我下载了它(没有安装它,因为我不感兴趣)我迅速浏览了源代码。
我在那段代码中看到了一些非常可疑的东西。以下是他们如何实现您调用的第一种方法:
@implementation OCMArg
....
+ (id *)setTo:(id)value
{
return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease];
}
所以他们返回id*
,这实际上只是id
。
对我来说,这是一个无意义/错误或试图操纵ObjC内部(即使没有记录,ObjC对象存储的第一件事实际上是指向对象类的指针,因此类型为Class
与id
兼容,因此以某种方式将指向对象的引用或引用对象的id
转换为Class*
或id*
)是有效的。我没有时间或兴趣去研究整个API以找出他们为什么这样做。它们实际上可能有一个很好的理由(例如,如果你只将结果传递给另一个知道它应该是什么的API,但你在这里做的不仅仅是这个)。而不是研究OCMock,我会试着向你解释我发生的事情(ObjC和ARC)。
id __autoreleasing *arg = [OCMArg setTo:mockData];
ARC在这行代码中绝对不会做任何事情。
您可以在上面看到这种方法。类OCMPassByRefSetter
是一个简单的类,只保留参数后保留它,因此保留mockData
。 OCMPassByRefSetter
已自动释放,并会在下一次排空时消失(释放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;
}