NSInvocations에 대해 배울 때 메모리 관리에 대한 이해가 부족한 것처럼 보입니다. 여기 NSInvocation 및 NSError - __autoreleasing 및 메모리 크래셔
은 샘플 프로젝트입니다 : 물론@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 객체를 alloc, init, autorelease하고 * 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:®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;
}
이있다 나는 내가 할 때 무엇을하고 있었는지 도대체 알고 있다고 생각대로 된 아이 오프닝 나를 위해 (당황 약간) 그것은 메모리 관리에왔다!
내 crashher를 해결하기 위해 할 수있는 최선의 방법은 NSError * 오류 변수를 init 메서드에서 꺼내 전역 변수로 만드는 것입니다. 이 때문에 ** errorPointer에서 __autoreleasing에서 __strong으로 속성을 변경해야했습니다. 그러나 특히 작업 대기열에서 NSInvocations를 여러 번 재사용 할 가능성이 높다는 점을 고려하면 수정 사항이 이상적이지 않음을 분명히 알 수 있습니다. 그것도 단지 종류는 오류가 dealloc'd되고 내 의심을 확인합니다.
최종 WTF로 __ 브리지 캐스트를 사용하여 약간 시도해 보았습니다.하지만 1. 내가 여기 필요한지 확실하지 않습니다. 2. 순열 후에 효과가있는 것을 찾을 수 없었습니다.
나는이 모든 것이 클릭되지 않는 이유를 더 잘 이해할 수 있도록 도움이되는 몇 가지 통찰력을 알고 싶습니다.
Gotcha! * error' 그래서 정말 내가'정적 NSError를 __strong로 NSError * 오류를 선언해야 아니, – edelaney05
아 (? 그것은 힙 변수 만들기도 합리적인 말을한다는 것입니다? 나는 ... 스택/힙 구현 세부라고 생각) 그것을 고정시키지 마십시오. 'NSInvocation'을 클래스의 공용 부분으로 삼지 마십시오. 버그는 유용하지 않게 된 후에 액세스 할 수 있기 때문에 발생합니다. * 반드시 *해야하는 경우 오류 변수를 클래스의 속성으로 만듭니다. (덧붙여 말하자면, 스택 대 힙 (heap)은 구현 세부 사항이 아니며 기본 객체 수명 개념입니다. –
오류 인수는 호출이 시작된 후에 만 유용합니다. 타이머 또는 작업 큐에서 호출을 수행하므로 오류 변수가 범위를 벗어날 가능성이 거의 있습니다. 그러나, 그것이 왜 충돌하고 있는지에 관한 일반적인 요지는 이제 명백하게 결정되었습니다 - 감사합니다! 이 모든 객체가'NSInvocation' 래퍼 인 경우 – edelaney05