Có cách nào để xác định dòng mã nhất định method
được gọi từ đó không?
Có cách nào để xác định dòng mã nhất định method
được gọi từ đó không?
Câu trả lời:
Tôi hy vọng rằng điều này sẽ giúp:
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]);
Trong mã được tối ưu hóa hoàn toàn, không có cách nào chắc chắn 100% để xác định người gọi đến một phương pháp nhất định. Trình biên dịch có thể sử dụng tối ưu hóa cuộc gọi đuôi trong khi trình biên dịch sử dụng lại một cách hiệu quả khung ngăn xếp của người gọi cho callee.
Để xem một ví dụ về điều này, hãy đặt một điểm ngắt trên bất kỳ phương thức nhất định nào bằng cách sử dụng gdb và nhìn vào dấu vết. Lưu ý rằng bạn không thấy objc_msgSend () trước mỗi lần gọi phương thức. Đó là bởi vì objc_msgSend () thực hiện một cuộc gọi đuôi đối với việc triển khai của mỗi phương thức.
Mặc dù bạn có thể biên dịch ứng dụng của mình không được tối ưu hóa, nhưng bạn sẽ cần các phiên bản không được tối ưu hóa của tất cả các thư viện hệ thống để tránh chỉ một vấn đề này.
Và đây chỉ là một vấn đề; thực tế, bạn đang hỏi "làm cách nào để phát minh lại CrashTracer hoặc gdb?". Một vấn đề rất khó mà các nghề nghiệp được tạo ra. Trừ khi bạn muốn "công cụ gỡ lỗi" trở thành sự nghiệp của mình, tôi khuyên bạn không nên đi theo con đường này.
Bạn thực sự đang cố gắng trả lời câu hỏi nào?
Sử dụng câu trả lời được cung cấp bởi intropedro , tôi đã nghĩ ra điều này:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
mà sẽ chỉ trả lại cho tôi lớp và chức năng gốc:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps - nếu hàm được gọi bằng hàm PerformSelector, kết quả sẽ là:
Origin: [NSObject performSelector:withObject:]
Chỉ cần viết một phương pháp sẽ làm điều này cho bạn:
- (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;
}
Câu trả lời của @ Intropedro phiên bản Swift 2.0 để tham khảo;
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])")
Nếu đó là vì lợi ích gỡ lỗi, hãy tập thói quen đặt NSLog(@"%s", __FUNCTION__);
Là dòng đầu tiên bên trong mỗi phương thức trong các lớp của bạn. Sau đó, bạn luôn có thể biết thứ tự của các lệnh gọi phương thức khi nhìn vào trình gỡ lỗi.
Bạn có thể truyền self
dưới dạng một trong các đối số cho hàm và sau đó lấy tên lớp của đối tượng người gọi bên trong:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
Bằng cách này, bạn có thể chuyển nó vào bất kỳ đối tượng nào có thể giúp bạn xác định vị trí của vấn đề.
Một phiên bản được tối ưu hóa một chút của câu trả lời tuyệt vời của @Roy Kronenfeld:
- (NSString *)findCallerMethod
{
NSString *callerStackSymbol = nil;
NSArray<NSString *> *callStackSymbols = [NSThread callStackSymbols];
if (callStackSymbols.count >= 2)
{
callerStackSymbol = [callStackSymbols objectAtIndex:2];
if (callerStackSymbol)
{
// Stack: 2 TerribleApp 0x000000010e450b1e -[TALocalDataManager startUp] + 46
NSInteger idxDash = [callerStackSymbol rangeOfString:@"-" options:kNilOptions].location;
NSInteger idxPlus = [callerStackSymbol rangeOfString:@"+" options:NSBackwardsSearch].location;
if (idxDash != NSNotFound && idxPlus != NSNotFound)
{
NSRange range = NSMakeRange(idxDash, (idxPlus - idxDash - 1)); // -1 to remove the trailing space.
callerStackSymbol = [callerStackSymbol substringWithRange:range];
return callerStackSymbol;
}
}
}
return (callerStackSymbol) ?: @"Caller not found! :(";
}
@ennuikiller
//Add this private instance method to the class you want to trace from
-(void)trace
{
//Go back 2 frames to account for calling this helper method
//If not using a helper method use 1
NSArray* stack = [NSThread callStackSymbols];
if (stack.count > 2)
NSLog(@"Caller: %@", [stack objectAtIndex:2]);
}
//Add this line to the method you want to trace from
[self trace];
Trong cửa sổ đầu ra, bạn sẽ thấy một cái gì đó như sau.
Người gọi: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
Bạn cũng có thể phân tích cú pháp chuỗi này để trích xuất thêm dữ liệu về khung ngăn xếp.
2 = Thread id
My App = Your app name
0x0004e8ae = Memory address of caller
-[IINClassroomInit buildMenu] = Class and method name of caller
+86 = Number of bytes from the entry point of the caller that your method was called
Nó được lấy từ Phương thức gọi điện xác định trong iOS .
Phiên bản Swift 4 của câu trả lời @Geoff H để sao chép và dán ;]
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet :CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
var array = Array(sourceString.components(separatedBy: separatorSet))
array = array.filter { $0 != "" }
print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")
Phiên bản Swift 3 của câu trả lời @Geoff H để tham khảo:
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet: CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
let array = NSMutableArray(array: sourceString.components(separatedBy: separatorSet))
array.remove("")
print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")