如何确定哪个对象/类正在调用全局Swift函数?

时间:2021-09-24 09:31:53

With this code:

使用此代码:

func externalFunc() {
    println("How can I know which object/class is calling me?")
}

class Test {
    func callExternalFunc() {
        externalFunc()
    }
}

In the Objective-C runtime objc_msgSend passes two hidden parameters to every message we send. They are self and _cmd. (Source)

在Objective-C运行时,objc_msgSend将两个隐藏参数传递给我们发送的每条消息。他们是自我和_cmd。 (资源)

In the above example, is there any way to know who is calling externalFunc?

在上面的例子中,有没有办法知道谁在调用externalFunc?

4 个解决方案

#1


14  

I'm not sure if there is a way to obtain this automatically, but you can get this info if you add a default param of type String to the function and set it to #function.

我不确定是否有办法自动获取此信息,但如果向函数添加String类型的默认参数并将其设置为#function,则可以获取此信息。

For example...

func externalFunc(callingFunctionName: String = #function) {
    println("Calling Function: \(callingFunctionName)")
}

Then you would call it without the added default param...

那么你可以在没有添加默认参数的情况下调用它...

let test = Test()
test.callExternalFunc()

And it would print the following...

它将打印以下内容......

"Calling Function: callExternalFunc()"

#2


6  

If you are willing to modify the method signature you could do something like below:

如果您愿意修改方法签名,可以执行以下操作:

func externalFunc(file: String = #file, line: Int = #line) {
    println("calling File:\(file) from Line:\(line)")
}

From apple's swift blog

来自apple的快速博客

Swift borrows a clever feature from the D language: these identifiers (__FILE__ & __LINE__ ) expand to the location of the caller when evaluated in a default argument list.

Swift借用了D语言的一个聪明的特性:当在默认参数列表中计算时,这些标识符(__FILE__和__LINE__)会扩展到调用者的位置。

Note that __FILE__ and __LINE__ have been depreciated in Swift 2.2 and have been removed in Swift 3. They are replaced by #file, and #line.

请注意,__FILE__和__LINE__已在Swift 2.2中折旧,并已在Swift 3中删除。它们被#file和#line取代。

#3


1  

Here's a great utility class I found on github:

这是我在github上找到的一个很棒的实用工具类:

https://github.com/nurun/swiftcallstacktrace

Use like this:

使用这样:

let callingMethodInfo = CallStackAnalyser.getCallingClassAndMethodInScope(false)

if let callingMethodInfo = callingMethodInfo {
   NSLog("class: %@", callingMethodInfo.0)
   NSLog("method: %@", callingMethodInfo.1)
}

#4


-1  

In your question you are mentioning self and _cmd.

在你的问题中,你提到自我和_cmd。

self is accessible in Swift exactly in the same way as in Obj-C (which is logical).

可以在Swift中以与Obj-C相同的方式访问self(这是合乎逻辑的)。

_cmd (selector of the current method) is not accessible. There is no reason for it to be accessible, Swift doesn't use selectors outside Obj-C contexts (in pure Swift you cannot call selectors dynamically). The only use case for it is to print the name of the current function for debugging purposes. The same can be achieved in Obj-C (or C) using __FUNCTION__ macro. The same can be achieved in Swift:

_cmd(当前方法的选择器)无法访问。没有理由可以访问它,Swift不使用Obj-C上下文之外的选择器(在纯Swift中你不能动态调用选择器)。唯一的用例是打印当前函数的名称以进行调试。使用__FUNCTION__宏可以在Obj-C(或C)中实现相同的效果。在Swift中也可以实现同样的目标:

func getCurrentFunctionName(functionName: StaticString = #function) -> String {   
    return String(functionName)
}

func externalFunc() {
    print("Function name: \(getCurrentFunctionName())") // prints "externalFunc"
}

Note that in your example externalFunc is a function, not a method. Even in Obj-C neither self or _cmd wouldn't be available for it.

请注意,在您的示例中,externalFunc是一个函数,而不是一个方法。即使在Obj-C中也不会有自我或_cmd。

If you want to know who has called your method (and I really suppose you want to know it for debugging purposes), then you can inspect your call stack:

如果你想知道谁调用了你的方法(我真的认为你想知道它用于调试目的),那么你可以检查你的调用堆栈:

func externalFunc() {
    let stackSymbols = NSThread.callStackSymbols()
    stackSymbols.forEach {
        let components = $0.stringByReplacingOccurrencesOfString(
            "\\s+",
            withString: " ",
            options: .RegularExpressionSearch,
            range: nil
        ).componentsSeparatedByString(" ")

        let name = components[3] 
        let demangledName = _stdlib_demangleName(name)    
        print("\(demangledName)")
    }
}

which prints (for my project called SwiftTest):

打印(对于我的项目称为SwiftTest):

SwiftTest.externalFunc () -> ()
SwiftTest.Test.callExternalFunc () -> ()
main
start
0x0

#1


14  

I'm not sure if there is a way to obtain this automatically, but you can get this info if you add a default param of type String to the function and set it to #function.

我不确定是否有办法自动获取此信息,但如果向函数添加String类型的默认参数并将其设置为#function,则可以获取此信息。

For example...

func externalFunc(callingFunctionName: String = #function) {
    println("Calling Function: \(callingFunctionName)")
}

Then you would call it without the added default param...

那么你可以在没有添加默认参数的情况下调用它...

let test = Test()
test.callExternalFunc()

And it would print the following...

它将打印以下内容......

"Calling Function: callExternalFunc()"

#2


6  

If you are willing to modify the method signature you could do something like below:

如果您愿意修改方法签名,可以执行以下操作:

func externalFunc(file: String = #file, line: Int = #line) {
    println("calling File:\(file) from Line:\(line)")
}

From apple's swift blog

来自apple的快速博客

Swift borrows a clever feature from the D language: these identifiers (__FILE__ & __LINE__ ) expand to the location of the caller when evaluated in a default argument list.

Swift借用了D语言的一个聪明的特性:当在默认参数列表中计算时,这些标识符(__FILE__和__LINE__)会扩展到调用者的位置。

Note that __FILE__ and __LINE__ have been depreciated in Swift 2.2 and have been removed in Swift 3. They are replaced by #file, and #line.

请注意,__FILE__和__LINE__已在Swift 2.2中折旧,并已在Swift 3中删除。它们被#file和#line取代。

#3


1  

Here's a great utility class I found on github:

这是我在github上找到的一个很棒的实用工具类:

https://github.com/nurun/swiftcallstacktrace

Use like this:

使用这样:

let callingMethodInfo = CallStackAnalyser.getCallingClassAndMethodInScope(false)

if let callingMethodInfo = callingMethodInfo {
   NSLog("class: %@", callingMethodInfo.0)
   NSLog("method: %@", callingMethodInfo.1)
}

#4


-1  

In your question you are mentioning self and _cmd.

在你的问题中,你提到自我和_cmd。

self is accessible in Swift exactly in the same way as in Obj-C (which is logical).

可以在Swift中以与Obj-C相同的方式访问self(这是合乎逻辑的)。

_cmd (selector of the current method) is not accessible. There is no reason for it to be accessible, Swift doesn't use selectors outside Obj-C contexts (in pure Swift you cannot call selectors dynamically). The only use case for it is to print the name of the current function for debugging purposes. The same can be achieved in Obj-C (or C) using __FUNCTION__ macro. The same can be achieved in Swift:

_cmd(当前方法的选择器)无法访问。没有理由可以访问它,Swift不使用Obj-C上下文之外的选择器(在纯Swift中你不能动态调用选择器)。唯一的用例是打印当前函数的名称以进行调试。使用__FUNCTION__宏可以在Obj-C(或C)中实现相同的效果。在Swift中也可以实现同样的目标:

func getCurrentFunctionName(functionName: StaticString = #function) -> String {   
    return String(functionName)
}

func externalFunc() {
    print("Function name: \(getCurrentFunctionName())") // prints "externalFunc"
}

Note that in your example externalFunc is a function, not a method. Even in Obj-C neither self or _cmd wouldn't be available for it.

请注意,在您的示例中,externalFunc是一个函数,而不是一个方法。即使在Obj-C中也不会有自我或_cmd。

If you want to know who has called your method (and I really suppose you want to know it for debugging purposes), then you can inspect your call stack:

如果你想知道谁调用了你的方法(我真的认为你想知道它用于调试目的),那么你可以检查你的调用堆栈:

func externalFunc() {
    let stackSymbols = NSThread.callStackSymbols()
    stackSymbols.forEach {
        let components = $0.stringByReplacingOccurrencesOfString(
            "\\s+",
            withString: " ",
            options: .RegularExpressionSearch,
            range: nil
        ).componentsSeparatedByString(" ")

        let name = components[3] 
        let demangledName = _stdlib_demangleName(name)    
        print("\(demangledName)")
    }
}

which prints (for my project called SwiftTest):

打印(对于我的项目称为SwiftTest):

SwiftTest.externalFunc () -> ()
SwiftTest.Test.callExternalFunc () -> ()
main
start
0x0