欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談在Swift中關(guān)于函數(shù)指針的實(shí)現(xiàn)

 更新時(shí)間:2015年07月16日 10:32:02   投稿:goldensun  
這篇文章主要介紹了淺談在Swift中關(guān)于函數(shù)指針的實(shí)現(xiàn),是作者根據(jù)C語(yǔ)言的指針特性在Swifft中做出的一個(gè)實(shí)驗(yàn),需要的朋友可以參考下

Swift沒(méi)有什么?

蘋果工程師給我建的唯一一堵墻是:在Swift中沒(méi)有任何辦法獲得一個(gè)函數(shù)的指針:

    注意,C函數(shù)指針不會(huì)導(dǎo)入到Swift中(來(lái)自“Using Swift with Cocoa and Objective-C“)

但是我們?cè)趺粗肋@種情況下鉤子的地址和跳到哪呢?讓我們深入了解一下,并且看看Swift的func在字節(jié)碼層面上的是什么。

當(dāng)你給一個(gè)函數(shù)傳遞一個(gè)泛型參數(shù)時(shí),Swift并沒(méi)有直接傳遞它的地址,而是一個(gè)指向trampoline函數(shù)(見(jiàn)下文)并帶有一些函數(shù)元數(shù)據(jù)信息的指針。并且trampoline自己是包裝原始函數(shù)的結(jié)構(gòu)的一部分。

這是什么意思?

讓我們用它來(lái)舉個(gè)例子:
 

復(fù)制代碼 代碼如下:

func call_function(f : () -> Int) {
    let b = f()
}
 
func someFunction() -> Int {
    return 0
}

在Swift里我們只寫(xiě) call_function(someFunction).
但是 Swift 編譯器處理代碼后,性能比調(diào)用call_function(&someFunction)好很多
 

復(fù)制代碼 代碼如下:

struct swift_func_wrapper *wrapper =  ... /* configure wrapper for someFunction() */
struct swift_func_type_metadata *type_metadata = ... /* information about function's arguments and return type */
call_function(wrapper->trampoline, type_metadata);

一個(gè)包裝器的結(jié)構(gòu)如下:  
 

復(fù)制代碼 代碼如下:

struct swift_func_wrapper {
    uint64_t **trampoline_ptr_ptr; // = &trampoline_ptr
    uint64_t *trampoline_ptr;
    struct swift_func_object *object;
}

什么是 swift_func_object類型? 為了創(chuàng)建對(duì)象,Swift 實(shí)時(shí)使用了一個(gè)全局的叫metadata[N]的的常量(每一個(gè) function調(diào)用都是唯一的,似的你的func 作為一個(gè)泛型的參數(shù),所以對(duì)于如下的代碼:  
 

復(fù)制代碼 代碼如下:

func callf(f: () -> ()) {
    f();
}
callf(someFunction);
callf(someFunction);

常量metadata和metadata2會(huì)被創(chuàng)建).

一個(gè)metadata[N]的結(jié)構(gòu)有點(diǎn)兒像這樣this:
 

復(fù)制代碼 代碼如下:

struct metadata {
    uint64_t *destructor_func;
    uint64_t *unknown0;
    const char type:1; // I'm not sure about this and padding,
    char padding[7];   // maybe it's just a uint64_t too...
    uint64_t *self;
}

最初metadataN只有2個(gè)字段集合:destructor_func 和 type。前者是一個(gè)函數(shù)指針,將用作為使用swift_allocObject() 創(chuàng)建的對(duì)象分配內(nèi)存。后者是對(duì)象類型識(shí)別器(函數(shù)或方法的0x40 或者 '@'),并且是(某種形式)被swift_allocObject() 用來(lái)創(chuàng)建一個(gè)正確的對(duì)象給我們的func: 
 
swift_allocObject(&metadata2->type, 0x20, 0x7);

一旦func 對(duì)象被創(chuàng)建,它擁有下面的結(jié)構(gòu):
 

復(fù)制代碼 代碼如下:

struct swift_func_object {
    uint64_t *original_type_ptr;
    uint64_t *unknown0;
    uint64_t function_address;
    uint64_t *self;
}

第一個(gè)字段是一個(gè)指針,用來(lái)對(duì)應(yīng)metadata[N]->type 的值,第二個(gè)字段似乎是 0x4 | 1 << 24(0x100000004) 并且暗示一些可能 (我不知道是什么)。  function_address 是我們實(shí)際掛鉤感興趣的地方,并且self 是 (立即) 自己的指針 (如果我們的對(duì)象表示一個(gè)普通的函數(shù),這個(gè)字段是 NULL)。


好,那么這段我從框架開(kāi)始如何?事實(shí)上,我不明白為什么Swift運(yùn)行時(shí)需要它們,但不論如何,這就是它們?cè)鷳B(tài)的樣子:
 

復(fù)制代碼 代碼如下:

void* someFunction_Trampoline(void *unknown, void *arg, struct swift_func_object *desc)
{
    void* target_function = (void *)desc->function_address;
    uint64_t *self = desc->self;
 
    swift_retain_noresult(desc->self); // yeah, retaining self is cool!
    swift_release(desc);
 
    _swift_Trampoline(unknown, arg, target_function, self);
    return unknown;
}
 
void *_swift_Trampoline(void *unknown, void *arg, void *target_function, void *self)
{
    target_function(arg, self);
    return unknown;
}

讓我們創(chuàng)建它

想象一下,在你的Swift代碼中有這些函數(shù):

 

復(fù)制代碼 代碼如下:

func takesFunc<T>(f : T) {
    ...
}
func someFunction() {
    ...
}

而且你想像這樣生成它們:
 

復(fù)制代碼 代碼如下:

takesFunc(someFunction)

這一行代碼會(huì)轉(zhuǎn)換成相當(dāng)大的C程序:
 

復(fù)制代碼 代碼如下:

struct swift_func_wrapper *wrapper = malloc(sizeof(*wrapper));
wrapper->trampoline_ptr     = &someFunction_Trampoline;
wrapper->trampoline_ptr_ptr = &(wrapper.trampoline);
wrapper->object = ({
    // let's say the metadata for this function is `metadata2`
    struct swift_func_object *object = swift_allocObject(&metadata2->type, 0x20, 0x7);
    object->function_address = &someFunction;
    object->self = NULL;
    object;
});
 
 
// global constant for the type of someFunction's arguments
const void *arg_type = &kSomeFunctionArgumentsTypeDescription;
// global constant for the return type of someFunction
const void *return_type = &kSomeFunctionReturnTypeDescription;
 
struct swift_func_type_metadata *type_metadata = swift_getFunctionTypeMetadata(arg_type, return_type);
 
takesFunc(wrapper->trampoline_ptr, type_metadata);

結(jié)構(gòu)體“swift_func_type_metadata”很不透明,因此我也沒(méi)太多可以說(shuō)的。

回到函數(shù)指針

既然我們已經(jīng)知道函數(shù)怎樣作為一個(gè)泛型類型參數(shù)表示,讓我們借助這個(gè)打到你的目的:獲取一個(gè)真正指向函數(shù)的指針!

我們要做的只是需要注意,我們已經(jīng)擁有一個(gè)作為第一個(gè)參數(shù)傳遞的trampoline_ptr指針域地址,所以object域的偏移量只是0x8。其他的所有都很容易組合:
 

復(fù)制代碼 代碼如下:

uint64_t _rd_get_func_impl(void *trampoline_ptr)
{
    struct swift_func_object *obj = (struct swift_func_object *)*(uint64_t *)(trampoline_ptr + 0x8);
 
    return obj->function_address;
}

看起來(lái)是時(shí)候?qū)憣?xiě)

 

復(fù)制代碼 代碼如下:

rd_route(
    _rd_get_func_impl(firstFunction),
    _rd_get_func_impl(secondFunction),
    nil
)

但我們?cè)鯓訌腟wift中調(diào)用這些C函數(shù)呢?

為此,我們將使用Swift非公開(kāi)的特性:允許我們提供給C函數(shù)一個(gè)Swift接口的@asmname屬性。用法如下:
 

復(fù)制代碼 代碼如下:

@asmname("_rd_get_func_impl")
    func rd_get_func_impl<Q>(Q) -> UInt64; 
 
@asmname("rd_route")
    func rd_route(UInt64, UInt64, CMutablePointer<UInt64>) -> CInt;

這就是我們?cè)赟wift中使用rd_route()需要的一切。

但是它不能處理任何函數(shù)!

也就是說(shuō),你不能用rd_route()鉤住任何帶有泛型參數(shù)的函數(shù)(這可能是Swift的bug,也可能不是,我還沒(méi)弄清楚)。但是你可以使用extensions輕松的覆蓋它們,直接指定參數(shù)的類型:
 

復(fù)制代碼 代碼如下:

class DemoClass {
    class func template <T : CVarArg>(arg : T, _ num: Int) -> String {
        return "\(arg) and \(num)";
    }
}
 
DemoClass.template("Test", 5) // "Test and 5"
 
extension DemoClass {
    class func template(arg : String, _ num: Int) -> String {
        return "{String}";
    }
    class func template(arg : Int, _ num: Int) -> String {
        return "{Int}";
    }
}
 
-- Your extension's methods for String and Int will be preferred over the original ones */
DemoClass.template("Test", 5) -- "{String}"
DemoClass.template(42, 5) -- "{Int}"
-- But for other types `template(T, Int)` will be used
DemoClass.template(["Array", "Item"], 5) --- "[Array, Item] and 5"

SWRoute

為了在Swift里輕松地勾住函數(shù),我創(chuàng)建了一個(gè)名為SWRoute的封裝體—它只是一個(gè)小類和一個(gè)我們之前寫(xiě)過(guò)的C函數(shù):

復(fù)制代碼 代碼如下:
_rd_get_func_impl():
 
class SwiftRoute {
    class func replace<MethodT>(function targetMethod : MethodT, with replacement : MethodT) -> Int
    {
        return Int(rd_route(rd_get_func_impl(targetMethod), rd_get_func_impl(replacement), nil));
    }
}

注意,我們無(wú)償進(jìn)行類型檢查因?yàn)镾wift需要目標(biāo)方法和替換具有相同的MethoT類型。

而且我們也無(wú)法使用一個(gè)復(fù)制的原始實(shí)現(xiàn),因此我只能把nil作為另一個(gè)參數(shù)傳給函數(shù)rd_route()。如果你對(duì)如何把這個(gè)指針集成到Swift代碼有自己的看法,麻煩告訴我!

你可以在資源庫(kù)中找到大量SWRoute的實(shí)例。

這就是所有的了。

相關(guān)文章

  • Combine中錯(cuò)誤處理和Scheduler使用詳解

    Combine中錯(cuò)誤處理和Scheduler使用詳解

    這篇文章主要為大家介紹了Combine中錯(cuò)誤處理和Scheduler使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • iOS Swift讀取本地json文件報(bào)錯(cuò)的解決方法

    iOS Swift讀取本地json文件報(bào)錯(cuò)的解決方法

    只要是app開(kāi)發(fā)者都知道,從服務(wù)器端獲得的數(shù)據(jù)要不就是json格式的數(shù)據(jù),要么就是xml格式的數(shù)據(jù),而這篇文章主要給大家介紹了關(guān)于iOS Swift讀取本地json文件報(bào)錯(cuò)的解決方法,需要的朋友可以參考借鑒,下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Swift仿選擇電影票的效果并實(shí)現(xiàn)無(wú)限/自動(dòng)輪播的方法

    Swift仿選擇電影票的效果并實(shí)現(xiàn)無(wú)限/自動(dòng)輪播的方法

    這篇文章主要給大家介紹了關(guān)于Swift仿選擇電影票的效果并實(shí)現(xiàn)無(wú)限/自動(dòng)輪播的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Swift中swift中的switch 語(yǔ)句

    Swift中swift中的switch 語(yǔ)句

    本文給大家介紹了swift中的swift語(yǔ)句,以及和c語(yǔ)音中的寫(xiě)法區(qū)別,本文介紹的非常詳細(xì),需要的朋友參考下
    2016-12-12
  • 詳解Swift面向?qū)ο缶幊讨械姆椒?method)

    詳解Swift面向?qū)ο缶幊讨械姆椒?method)

    既然面向?qū)ο竽蔷鸵欢〞?huì)有method,方法和面向過(guò)程語(yǔ)言中的function函數(shù)并沒(méi)什么區(qū)別,只不過(guò)方法在面向?qū)ο笳Z(yǔ)言中可以被類來(lái)約束作用域,這里我們就來(lái)詳解Swift面向?qū)ο缶幊讨械姆椒?method)
    2016-07-07
  • swift中可選值?和!使用的方法示例

    swift中可選值?和!使用的方法示例

    這篇文章主要給大家介紹了關(guān)于swift中可選值?和!使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • 詳談swift內(nèi)存管理中的引用計(jì)數(shù)

    詳談swift內(nèi)存管理中的引用計(jì)數(shù)

    下面小編就為大家?guī)?lái)一篇詳談swift內(nèi)存管理中的引用計(jì)數(shù)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • Swift UILable 設(shè)置內(nèi)邊距實(shí)例代碼

    Swift UILable 設(shè)置內(nèi)邊距實(shí)例代碼

    本文主要介紹Swift UILable 設(shè)置內(nèi)邊距,這里提供示例代碼供大家參考,有需要的小伙伴可以看下
    2016-07-07
  • SpringBoot3.0集成Redis緩存的實(shí)現(xiàn)示例

    SpringBoot3.0集成Redis緩存的實(shí)現(xiàn)示例

    緩存就是一個(gè)存儲(chǔ)器,常用 Redis作為緩存數(shù)據(jù)庫(kù),本文主要介紹了SpringBoot3.0集成Redis緩存的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Swift源碼解析之弱引用

    Swift源碼解析之弱引用

    這篇文章主要給大家介紹了關(guān)于Swift源碼解析之弱引用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評(píng)論