gpt4 book ai didi

objective-c - NSInvocation & NSError - __autoreleasing & memory crasher

转载 作者:太空狗 更新时间:2023-10-30 04:02:22 24 4
gpt4 key购买 nike

在学习 NSInvocations 时,我对内存管理的理解似乎存在差距。

这是一个示例项目:

@interface DoNothing : NSObject
@property (nonatomic, strong) NSInvocation *invocation;
@end

@implementation DoNothing
@synthesize invocation = _invocation;

NSString *path = @"/Volumes/Macintosh HD/Users/developer/Desktop/string.txt";

- (id)init
{
self = [super init];
if (self) {

SEL selector = @selector(stringWithContentsOfFile:encoding:error:);
NSInvocation *i = [NSInvocation invocationWithMethodSignature:[NSString methodSignatureForSelector:selector]];

Class target = [NSString class];
[i setTarget:target];
[i setSelector:@selector(stringWithContentsOfFile:encoding:error:)];

[i setArgument:&path atIndex:2];

NSStringEncoding enc = NSASCIIStringEncoding;
[i setArgument:&enc atIndex:3];

__autoreleasing NSError *error;
__autoreleasing NSError **errorPointer = &error;
[i setArgument:&errorPointer atIndex:4];

// I understand that I need to declare an *error in order to make sure
// that **errorPointer points to valid memory. But, I am fuzzy on the
// __autoreleasing aspect. Using __strong doesn't prevent a crasher.

[self setInvocation:i];
}

return self;
}

@end

当然,我在这里所做的只是构建一个调用对象作为 NSString 类方法的属性

+[NSString stringWithContentsOfFile:(NSString \*)path encoding:(NSStringEncoding)enc error:(NSError \**)error]

很有道理,尤其是在阅读之后this blog post ,至于为什么我需要通过声明并将地址分配给 **errorPointer 来处理 NSError 对象。有点难以理解的是这里发生的 __autoreleasing 和内存管理。

**errorPointer 变量不是对象,因此它没有保留计数。它只是存储指向 NSError 对象的内存地址的内存。我知道 stringWith... 方法将分配、初始化和自动释放一个 NSError 对象,并设置 *errorPointer = 分配的内存。正如您稍后将看到的,NSError 对象变得不可访问。这是……

  • ...因为自动释放池已耗尽?
  • ...因为 ARC 填充了对 stringWith...的 alloc + init 的“release”调用?

那么让我们来看看调用是如何“工作”的

int main(int argc, const char * argv[])
{
@autoreleasepool {

NSError *regularError = nil;
NSString *aReturn = [NSString stringWithContentsOfFile:path
encoding:NSASCIIStringEncoding
error:&regularError];

NSLog(@"%@", aReturn);

DoNothing *thing = [[DoNothing alloc] init];
NSInvocation *invocation = [thing invocation];

[invocation invoke];

__strong NSError **getErrorPointer;
[invocation getArgument:&getErrorPointer atIndex:4];
__strong NSError *getError = *getErrorPointer; // CRASH! EXC_BAD_ACCESS

// It doesn't really matter what kind of attribute I set on the NSError
// variables; it crashes. This leads me to believe that the NSError
// object that is pointed to is being deallocated (and inspecting with
// NSZombies on, confirms this).

NSString *bReturn;
[invocation getReturnValue:&bReturn];
}
return 0;
}

这让我大开眼界(有点令人不安),因为我认为我知道在内存管理方面我到底在做什么!

解决崩溃问题的最佳方法是从 init 方法中拉出 NSError *error 变量,并使其成为全局变量。这要求我将 **errorPointer 上的属性从 __autoreleasing 更改为 __strong。但是,很明显,该修复并不理想,特别是考虑到可能会在操作队列中多次重用 NSInvocations。它也只是有点证实了我对 *error 正在被释放的怀疑。

作为最终的 WTF,我尝试了一些 __bridge 类型转换,但是 1. 我不确定这是否是我在这里需要的,以及 2. 排列后我找不到一个有效的。

我希望有一些见解可以帮助我更好地理解为什么这一切都没有点击。

最佳答案

这实际上是一个非常简单的错误,与自动引用计数无关。

-[DoNothing init] 中,您使用指向堆栈变量的指针初始化调用的错误参数:

__autoreleasing NSError *error;
__autoreleasing NSError **errorPointer = &error;
[i setArgument:&errorPointer atIndex:4];

而在 main 中,您正在获取同一个指针并取消引用它:

__strong NSError **getErrorPointer;
[invocation getArgument:&getErrorPointer atIndex:4];
__strong NSError *getError = *getErrorPointer;

当然,此时 -[DoNothing init] 中的所有局部变量都不再存在,并且尝试从其中读取会导致崩溃。

关于objective-c - NSInvocation & NSError - __autoreleasing & memory crasher,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10002538/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com