[ios] Objective C 메서드 호출자 찾기

특정 코드 method가 호출 된 줄을 확인하는 방법이 있습니까?



답변

이것이 도움이되기를 바랍니다.

    NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1];
    // Example: 1   UIKit                               0x00540c89 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163
    NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"];
    NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString  componentsSeparatedByCharactersInSet:separatorSet]];
    [array removeObject:@""];

    NSLog(@"Stack = %@", [array objectAtIndex:0]);
    NSLog(@"Framework = %@", [array objectAtIndex:1]);
    NSLog(@"Memory address = %@", [array objectAtIndex:2]);
    NSLog(@"Class caller = %@", [array objectAtIndex:3]);
    NSLog(@"Function caller = %@", [array objectAtIndex:4]);


답변

완전히 최적화 된 코드에서는 특정 메서드에 대한 호출자를 결정하는 100 % 확실한 방법이 없습니다. 컴파일러는 마무리 호출 최적화를 사용할 수 있지만 컴파일러는 호출 수신자에 대해 호출자의 스택 프레임을 효과적으로 재사용합니다.

이에 대한 예를 보려면 gdb를 사용하여 주어진 메소드에 중단 점을 설정하고 역 추적을 살펴보십시오. 모든 메서드 호출 전에 objc_msgSend ()가 표시되지는 않습니다. 그 이유는 objc_msgSend ()가 각 메서드의 구현에 대한 마무리 호출을 수행하기 때문입니다.

최적화되지 않은 애플리케이션을 컴파일 할 수 있지만이 문제를 방지하려면 모든 시스템 라이브러리의 최적화되지 않은 버전이 필요합니다.

그리고 이것은 단지 하나의 문제 일뿐입니다. 실제로 “CrashTracer 또는 gdb를 어떻게 재창조합니까?”라고 묻는 것입니다. 경력을 쌓는 매우 어려운 문제입니다. “디버깅 도구”가 귀하의 경력이되기를 원하지 않는 한, 저는이 길을가는 것을 권장하지 않습니다.

정말로 대답하려고하는 질문은 무엇입니까?


답변

intropedro가 제공 한 답변을 사용하여 다음 과 같이 생각해 냈습니다.

#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])

간단히 원래 클래스와 기능을 반환합니다.

2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]

ps-performSelector를 사용하여 함수를 호출하면 결과는 다음과 같습니다.

Origin: [NSObject performSelector:withObject:]


답변

이 작업을 수행하는 방법을 작성했습니다.

- (NSString *)getCallerStackSymbol {

    NSString *callerStackSymbol = @"Could not track caller stack symbol";

    NSArray *stackSymbols = [NSThread callStackSymbols];
    if(stackSymbols.count >= 2) {
        callerStackSymbol = [stackSymbols objectAtIndex:2];
        if(callerStackSymbol) {
            NSMutableArray *callerStackSymbolDetailsArr = [[NSMutableArray alloc] initWithArray:[callerStackSymbol componentsSeparatedByString:@" "]];
            NSUInteger callerStackSymbolIndex = callerStackSymbolDetailsArr.count - 3;
            if (callerStackSymbolDetailsArr.count > callerStackSymbolIndex && [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex]) {
                callerStackSymbol = [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex];
                callerStackSymbol = [callerStackSymbol stringByReplacingOccurrencesOfString:@"]" withString:@""];
            }
        }
    }

    return callerStackSymbol;
}


답변

참조 용 @Intropedro의 답변의 Swift 2.0 버전;

let sourceString: String = NSThread.callStackSymbols()[1]

let separatorSet :NSCharacterSet = NSCharacterSet(charactersInString: " -[]+?.,")
let array = NSMutableArray(array: sourceString.componentsSeparatedByCharactersInSet(separatorSet))
array.removeObject("")

print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")


답변

디버깅을 목적으로하는 경우에는 NSLog(@"%s", __FUNCTION__);

클래스의 각 메서드 내부의 첫 번째 줄로. 그러면 항상 디버거를보고 메서드 호출 순서를 알 수 있습니다.


답변

self함수에 인수 중 하나로 전달한 다음 내부에서 호출자 개체의 클래스 이름을 가져올 수 있습니다.

+(void)log:(NSString*)data from:(id)sender{
    NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}

//...

-(void)myFunc{
    [LoggerClassName log:@"myFunc called" from:self];
}

이렇게하면 문제의 위치를 ​​파악하는 데 도움이되는 모든 개체를 전달할 수 있습니다.