- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在学习 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 对象变得不可访问。这是……
那么让我们来看看调用是如何“工作”的
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSError *regularError = nil;
NSString *aReturn = [NSString stringWithContentsOfFile:path
encoding:NSASCIIStringEncoding
error:®ularError];
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/
在学习 NSInvocations 时,我对内存管理的理解似乎存在差距。 这是一个示例项目: @interface DoNothing : NSObject @property (nonatomic,
我有一个 UIViewController,它的 View 属性中有几个 subview (UISearchbar 和几个 UIButton)。 UIButton 连接到典型的 IBAction,例如
我有一个崩溃问题,它在我们的生产应用程序中经常发生,但我们无法重现它。它仅发生在使用 iOS8 SDK 的 iOS8 上。 HTTPReadFilter::doPlainRead(StreamRead
我是一名优秀的程序员,十分优秀!