JavaScriptCore全面解析 (下篇)

来源:http://www.cnblogs.com/liuliliuli2017/archive/2017/04/28/6782773.html
-Advertisement-
Play Games

收錄待用,修改轉載已取得 "騰訊雲" 授權 作者 | 殷源 編輯 | 迷鹿 殷源,專註移動客戶端開發,微軟Imagine Cup中國區特等獎獲得者,現就職於騰訊。 接 "JavaScriptCore全面解析 (上篇)" 六、 JSExport JSExport協議提供了一種聲明式的方法去向 "Jav ...


收錄待用,修改轉載已取得騰訊雲授權


作者 | 殷源
編輯 | 迷鹿

殷源,專註移動客戶端開發,微軟Imagine Cup中國區特等獎獲得者,現就職於騰訊。

JavaScriptCore全面解析 (上篇)

六、 JSExport

JSExport協議提供了一種聲明式的方法去向JavaScript代碼導出Objective-C的實例類及其實例方法,類方法和屬性。

1. 在JavaScript中調用native代碼

兩種方式:

  • Block

  • JSExport

Block的方式很簡單,如下:

context[@"add"] = ^(NSInteger a, NSInteger b) {
    return a+b;
};

JSValue *resultValue = [context evaluateScript:@"add(5, 6)"];
//另外一種調用JS函數的方法
resultValue = [context[@"add"] callWithArguments:@[@(5), @(6)]];
NSLog(@"resultValue = %@", resultValue);

Output:

11

JSExport的方式需要通過繼承JSExport協議的方式來導出指定的方法和屬性:

@class MyPoint;

@protocol MyPointExports <JSExport>
@property double x;
@property double y;
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end

@interface MyPoint : NSObject <MyPointExports>
- (void)myPrivateMethod; // Not in the MyPointExports protocol, so
    not visible to JavaScript code.
+ (void)test;
@endltValue);

繼承於JSExport協議的MyPointExports協議中的實例變數,實例方法和類方法都會被導出,而MyPoint類的- (void)myPrivateMethod方法卻不會被導出。

在OC代碼中我們這樣導出:

//導出對象
context[@"point"] = [[MyPoint alloc] initWithX:6 y:8];

//導出類
context[@"MyPoint"] = [MyPoint class];

在JS代碼中可以這樣調用:

// Objective-C properties become fields. 
point.x; 

point.x = 10; 

// Objective-C instance methods become functions. 
point.description(); 

// Objective-C initializers can be called with constructor syntax. 
var p = MyPoint(1, 2); 

// Objective-C class methods become functions on the constructor object. 
var q = MyPoint.makePointWithXY(0, 0);

2. 導出OC方法和屬性給JS

  • 預設情況下,一個Objective-C類的方法和屬性是不會導出給JavaScript的。你必須選擇指定的方法和屬性來導出。對於一個class實現的每個協議,如果這個協議繼承了JSExport協議,JavaScriptCore就將這個協議的方法和屬性列表導出給JavaScript。

  • 對於每一個導出的實例方法,JavaScriptCore都會在prototype中創建一個存取器屬性。對於每一個導出的類方法,JavaScriptCore會在constructor對象中創建一個對應的JavaScript function。

  • 在Objective-C中通過@property聲明的屬性決定了JavaScript中的對應屬性的特征:

  • Objective-C類中的屬性,成員變數以及返回值都將根據JSValue指定的拷貝協議進行轉換。

3. 函數名轉換

轉換成駝峰形式:

  • 去掉所有的冒號

  • 所有冒號後的第一個小寫字母都會被轉為大寫

4. 自定義導出函數名

如果不喜歡預設的轉換規則,也可以使用JSExportAs來自定義轉換

5. 導出OC對象給JS

  • 如何導出自定義的對象?

  • 自定義對象有複雜的繼承關係是如何導出的?

在討論這個話題之前,我們首先需要對JavaScript中的對象與繼承關係有所瞭解。

七、 JavaScript對象繼承

如果你已經瞭解JavaScript的對象繼承,可以跳過本節。

這裡會快速介紹JavaScript對象繼承的一些知識:

1. JavaScript的數據類型

最新的 ECMAScript 標准定義了 7 種數據類型:

6 種 原始類型:

  • Boolean

  • Null

  • Undefined

  • Number

  • String

  • Symbol (ECMAScript 6 新定義)和 Object

2. JavaScript原始值

除 Object 以外的所有類型都是不可變的(值本身無法被改變)。我們稱這些類型的值為“原始值”。

  • 布爾類型:兩個值:true 和 false

  • Null 類型:只有一個值: null

  • Undefined 類型:一個沒有被賦值的變數會有個預設值 undefined

  • 數字類型

  • 字元串類型:不同於類 C 語言,JavaScript 字元串是不可更改的。這意味著字元串一旦被創建,就不能被修改

  • 符號類型

3. JavaScript對象

在 Javascript 里,對象可以被看作是一組屬性的集合。這些屬性還可以被增減。屬性的值可以是任意類型,包括具有複雜數據結構的對象。

以下代碼構造了一個point對象:

var point = { 
    x : 99, 
    y : 66, 
    revers : function() { 
        var tmp = this.x 
        this.x = this.y 
        this.y = tmp 
    }, 
    name : 'BiuBiuBiu', 
    next : null 
} 

point.revers();

4. JavaScript屬性

ECMAScript定義的對象中有兩種屬性:數據屬性和訪問器屬性。

  • 數據屬性

數據屬性是鍵值對,並且每個數據屬性擁有下列特性:

  • 訪問器屬性

訪問器屬性有一個或兩個訪問器函數 (get 和 set) 來存取數值,並且有以下特性:

5. JavaScript屬性設置與檢測

  • 設置一個對象的屬性會只會修改或新增其自有屬性,不會改變其繼承的同名屬性

  • 調用一個對象的屬性會依次檢索本身及其繼承的屬性,直到檢測到

var point = {x:99, y:66}; 
var childPoint = Object.create(point); 
console.log(childPoint.x) 
childPoint.x = 88 
console.log(childPoint.x)

Output:

99
88

在chrome的控制臺中,我們分別列印設置x屬性前後point對象的內部結構:

設置前

設置後

!

可見,設置一個對象的屬性並不會修改其繼承的屬性,只會修改或增加其自有屬性。

這裡我們談到了proto和繼承屬性,下麵我們詳細講解。

八、 Prototype

JavaScript對於有基於類的語言經驗的開發人員來說有點令人困惑 (如Java或C ++) ,因為它是動態的,並且本身不提供類實現。(在ES2015/ES6中引入了class關鍵字,但是只是語法糖,JavaScript 仍然是基於原型的)。

當談到繼承時,Javascript 只有一種結構:對象。每個對象都有一個內部鏈接到另一個對象,稱為它的原型 prototype。該原型對象有自己的原型,等等,直到達到一個以null為原型的對象。根據定義,null沒有原型,並且作為這個原型鏈 prototype chain中的最終鏈接。

任何一個對象都有一個proto屬性,用來表示其繼承了什麼原型。

以下代碼定一個具有繼承關係的對象,point對象繼承了一個具有x,y屬性的原型對象。

var point = { 
    name : null, 
    __proto__ : { 
        x:99, 
        y:66, 
        __proto:Object.prototype 
    } 
}

Object.prototype.__proto__ == null        \\true

在Chrome的控制臺中,我們列印對象結構:

可見繼承關係,point繼承的原型又繼承了Object.prototype,而Object.prototypeproto指向null,因而它是繼承關係的終點。
這裡我們首先要知道prototype和proto是兩種屬性,前者只有function才有,後者所有的對象都有。後面會詳細講到。

1. JavaScript類?

Javascript 只有一種結構:對象。類的概念又從何而來?

在JavaScript中我們可以通過function來模擬類,例如我們定義一個MyPoint的函數,並把他認作MyPoint類,就可以通過new來創建具有x,y屬性的對象

function MyPoint(x, y) { 
    this.x = x; 
    this.y = y; 
} 

var point = new MyPoint(99, 66);

列印point對象結構:

這裡出現一個constructor的概念

2. JavaScript constructor

每個JavaScript函數都自動擁有一個prototype的屬性,這個prototype屬性是一個對象,這個對象包含唯一一個不可枚舉屬性constructor。constructor屬性值是一個函數對象

執行以下代碼我們會發現對於任意函數F.prototype.constructor == F

var F = function(){}; //一個函數對象F 

var p = F.prototype; //F關聯的原型對象 

var c = p.constructor; //原型對象關聯的constructor函數

c == F // =>true: 對於任意函數F.prototype.constructor == F

這裡即存在一個反向引用的關係:

3. new發生了什麼?

當調用new MyPoint(99, 66)時,虛擬機生成了一個point對象,並調用了MyPoint的prototype的constructor對象對point進行初始化,並且自動將MyPoint.prototype作為新對象point的原型。
相當於下麵的偽代碼

var point ;
point = MyPoint.prototype.constructor(99,66);
point.__proto__ = MyPoint.prototype;

4. _ proto __ 與prototype

簡單地說:

  • _proto__是所有對象的屬性,表示對象自己繼承了什麼對象

  • prototype是Function的屬性,決定了new出來的新對象的proto

如圖詳細解釋了兩者的區別

!

5. 列印JavaScript對象結構

  • 在瀏覽器提供的JavaScript調試工具中,我們可以很方便地列印出JavaScript對象的內部結構

  • 在Mac/iOS客戶端JavaScriptCore中並沒有這樣的列印函數,這裡我自定義了一個列印函數。鑒於對象的內部結構容易出現迴圈引用導致迭代列印陷入死迴圈,我們在這裡簡單地處理,對屬性不進行迭代列印。為了描述對象的原型鏈,這裡手動在對象末尾對其原型進行列印。

function __typeof__(objClass)
{
    if ( objClass && objClass.constructor )
    {
        var strFun = objClass.constructor.toString();
        var className = strFun.substr(0, strFun.indexOf('('));
        className = className.replace('function', '');
        return className.replace(/(^\s*)|(\s*$)/ig, '');
    }
    return typeof(objClass);
}

function dumpObj(obj, depth) {

    if (depth == null || depth == undefined) {
        depth = 1;
    }
    if (typeof obj != "function" && typeof obj != "object") {
        return '('+__typeof__(obj)+')' + obj.toString();
    }

    var tab = '    ';
    var tabs = '';
    for (var i = 0; i<depth-1; i++) {
        tabs+=tab;
    }

    var output = '('+__typeof__(obj)+') {\n';

    var names = Object.getOwnPropertyNames(obj);
    for (index in names) {
        var propertyName = names[index];

        try {
            var property = obj[propertyName];
            output += (tabs+tab+propertyName + ' = ' + '('+__typeof__(property)+')' +property.toString()+ '\n');
        }catch(err) {
            output += (tabs+tab+propertyName + ' = ' + '('+__typeof__(property)+')' + '\n');
        }
    }

    var prt = obj.__proto__;
    if (typeof obj == "function") {
        prt = obj.prototype;
    }

    if (prt!=null && prt!= undefined) {
        output += (tabs+tab+'proto = ' + dumpObj(prt, depth+1) + '\n');
    }else {
        output += (tabs+tab+'proto = '+prt+' \n');
    }

    output+=(tabs+'}');
    return output;
}

function printObj(obj) {
    log(dumpObj(obj));
}

6. log

我們為所有的context都添加一個log函數,方便我們在JS中向控制台輸出日誌

context[@"log"] = ^(NSString *log) {
        NSLog(@"%@", log);
};

九、 導出OC對象給JS

現在我們繼續回到Objective-C中,看下OC對象是如何導出的

1. 簡單對象的導出

當你從一個未指定拷貝協議的Objective-C實例創建一個JavaScript對象時,JavaScriptCore會創建一個JavaScript的wrapper對象。對於具體類型,JavaScriptCore會自動拷貝值到合適的JavaScript類型。

以下代碼定義了一個繼承自NSObject的簡單類

@interface DPoint : NSObject

@property (nonatomic, retain) NSString *type;

@end

導出對象

DPoint *dPoint = [[DPoint alloc] init];
dPoint.type = @"Hello Point!";
//導出對象
context[@"d_point"] = dPoint;
[context evaluateScript:@"printObj(d_point)"];

然後我們列印JavaScript中的d_point對象結構如下:

//Output
() { 
    proto = () { 
        constructor = (Object)[object DPointConstructor] 
        proto = (Object) { 
            toString = (Function)function toString() { [native code] } 
            toLocaleString = (Function)function toLocaleString() { [native code] } 
            valueOf = (Function)function valueOf() { [native code] } 
            hasOwnProperty = (Function)function hasOwnProperty() { [native code] } 
            propertyIsEnumerable = (Function)function propertyIsEnumerable() { [native code] } 
            isPrototypeOf = (Function)function isPrototypeOf() { [native code] } 
            __defineGetter__ = (Function)function __defineGetter__() { [native code] } 
            __defineSetter__ = (Function)function __defineSetter__() { [native code] } 
            __lookupGetter__ = (Function)function __lookupGetter__() { [native code] } 
            __lookupSetter__ = (Function)function __lookupSetter__() { [native code] } 
            __proto__ = (object) 
            constructor = (Function)function Object() { [native code] } 
            proto = null 
        } 
    } 
}

可見,其type屬性並沒有被導出。

JS中的對象原型是就是Object.prototype。

2. 繼承關係的導出

在JavaScript中,繼承關係是通過原型鏈(prototype chain)來支持的。對於每一個導出的Objective-C類,JavaScriptCore會在context中創建一個prototype。對於NSObject類,其prototype對象就是JavaScript context的Object.prototype。

對於所有其他的Objective-C類,JavaScriptCore會創建一個prototype屬性指向其父類的原型屬性的原型對象。如此,JavaScript中的wrapper對象的原型鏈就反映了Objective-C中類型的繼承關係。

我們讓DPoint繼承子MyPoint

@interface DPoint : MyPoint

@property (nonatomic, retain) NSString *type;

@end

在OC中,它的繼承關係是這樣的

在JS中,它的繼承關係是這樣的

列印對象結構來驗證:

//導出類
context[@“DPoint"] = [DPoint class] ;
[context evaluateScript:@“log(Dpoint.prototype.constructor==DPoint)"];
[context evaluateScript:@"printObj(DPoint)"];

Output:

true
(Function) { 
    name = (String)DPoint 
    prototype = (DPoint)[object DPointPrototype] 
    proto = (DPoint) { 
        constructor = (Function)function DPoint() { [native code] } 
        proto = (MyPoint) { 
            constructor = (Function)function MyPoint() { [native code] } 
            description = (Function)function () { [native code] } 
            x = (Function) 
            y = (Function) 
            proto = (Object) {
        toString = (Function)function toString() { [native code] } 
        toLocaleString = (Function)function toLocaleString() { [native code] } 
        ……        
        __proto__ = (object) 
        constructor = (Function)function Object() { [native code] } 
        proto = null 
    } 
        } 
    } 
}

可見,DPoint自身的未導出的屬性type沒有在JS對象中反應出來,其繼承的MyPoint的導出的屬性和函數都在JS對象的原型中。

十、 記憶體管理

1. 迴圈引用

之前已經講到, 每個JSValue對象都持有其JSContext對象的強引用,只要有任何一個與特定JSContext關聯的JSValue被持有(retain),這個JSContext就會一直存活。如果我們將一個native對象導出給JavaScript,即將這個對象交由JavaScript的全局對象持有
,引用關係是這樣的:

這時如果我們在native對象中強引用持有JSContext或者JSValue,便會造成迴圈引用:

因此在使用時要註意以下幾點:

2. 避免直接使用外部context

  • 避免在導出的block/native函數中直接使用JSContext

  • 使用 [JSContext currentContext] 來獲取當前context能夠避免迴圈引用

//錯誤用法
context[@"block"] = ^() {
    NSLog(@"%@", context);
};

//糾正用法
context[@"block"] = ^() {
    NSLog(@"%@", [JSContext currentContext]);
};

3. 避免直接使用外部JSValue

  • 避免在導出的block/native函數中直接使用JSValue
//錯誤用法
JSValue *value = [JSValue valueWithObject:@"test“ inContext:context];
context[@"block"] = ^(){
    NSLog(@"%@", value);
};

//糾正用法
JSValue *value = [JSValue valueWithObject:@"test“ inContext:context];
JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value     andOwner:self];
context[@"block"] = ^(){
    NSLog(@"%@", [managedValue value]);
};

這裡我們使用了JSManagedValue來解決這個問題

十一、 JSManagedValue

  • 一個JSManagedValue對象包含了一個JSValue對象,“有條件地持有(conditional retain)”的特性使其可以自動管理記憶體。

  • 最基本的用法就是用來在導入到JavaScript的native對象中存儲JSValue。

  • 不要在在一個導出到JavaScript的native對象中持有JSValue對象。因為每個JSValue對象都包含了一個JSContext對象,這種關係將會導致迴圈引用,因而可能造成記憶體泄漏。

1. 有條件地持有

所謂“有條件地持有(conditional retain)”,是指在以下兩種情況任何一個滿足的情況下保證其管理的JSValue被持有:可以通過JavaScript的對象圖找到該JSValue

  • 可以通過native對象圖找到該JSManagedValue。使用addManagedReference:withOwner:方法可向虛擬機記錄該關係反之,如果以上條件都不滿足,JSManagedValue對象就會將其value置為nil並釋放該JSValue。

  • JSManagedValue對其包含的JSValue的持有關係與ARC下的虛引用(weak reference)類似。

2. 為什麼不直接用虛引用?

通常我們使用weak來修飾block內需要使用的外部引用以避免迴圈引用,由於JSValue對應的JS對象記憶體由虛擬機進行管理並負責回收,這種方法不能準確地控制block內的引用JSValue的生命周期,可能在block內需要使用JSValue的時候,其已經被虛擬機回收。

API Reference

/* 可以直接使用JSManagedValue的類方法直接生產一個帶owner的對象 */
+ managedValueWithValue:andOwner:

/* 也可以使用JSVirtualMachine的實例方法來手動管理 */
addManagedReference:withOwner: 
removeManagedReference:withOwner:

/* owner即JSValue在native代碼中依托的對象,虛擬機就是通過owner來確認native中的對象圖關係 */

十二、 異常處理

  • JSContext的exceptionHandler屬性可用來接收JavaScript中拋出的異常

  • 預設的exceptionHandler會將exception設置給context的exception屬性

  • 因此,預設的表現就是從JavaScript中拋給native的未處理的異常又被拋回到JavaScript中,異常並未被捕獲處理。

  • 將context.exception設置為nil將會導致JavaScript認為異常已經被捕獲處理。

@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);

context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
    NSLog(@"exception : %@", exception);
    context.exception = exception;
};

參考:

https://trac.webkit.org/wiki/JavaScriptCore

https://trac.webkit.org/browser/trunk/Source/JavaScriptCore

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

https://developer.apple.com/reference/javascriptcore

http://blog.iderzheng.com/introduction-to-ios7-javascriptcore-framework/

http://blog.iderzheng.com/ios7-objects-management-in-javascriptcore-framework/


原文鏈接:https://www.qcloud.com/community/article/516026


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 歷時兩個多月的時間,終於把effective c++又複習了一遍,比較慢,看的是英文版,之前看的時候做過一些筆記,但不夠詳細,這次筆者是從頭到尾的翻譯了一遍,加了一些標題,先記錄到word裡面,然後發佈到博客園上。這麼做是為了方便查閱,複習C++,同時練習英文,希望這些帖子也能夠對大家有所幫助。 有 ...
  • 什麼是觀察者模式 舉個例子來簡單說明下這個模式:假如現在你在一家報社訂閱了報紙,每當有新的期刊,那麼他們就會把報紙送到你家,如果你什麼時候不想看這一期刊的時候,你就可以取消訂閱,那麼這時候他們就不會將報紙送到你家了。這其實就是利用了觀察者模式,先給出兩個基本概念:主題,就相當於被觀察的對象,這裡指的 ...
  • 每天一個設計模式 -9 裝飾者模式 一、現實 使用繼承不總能夠實現最有彈性和最好維護的設計。 利用組合和委托可以在運行時具有繼承行為的效果。 利用繼承設計子類的行為,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行為。 利用組合的做法擴展對象的行為,就可以在運行時動態地進行擴展。 二、認識裝飾 ...
  • 一、 基本概述 問題:假設有一個控制器,該控制器上有7個可編程的插槽,每個都可以指定到一個不同的家電裝置,每個插槽都有對應的開關按鈕。這個遙控器還具備一個整體的撤銷按鈕。廠家已經提供了控制家電基本處理類。希望你能夠創建一組控制遙控器的API,讓每個插槽都能夠控制一個或一組裝置。(如下圖,廠商類) 分 ...
  • java 企業網站源碼 前後臺都有 靜態模版引擎, 代碼生成器大大提高開發效率 前臺: 支持兩套模版, 可以在後臺切換 系統介紹: 1.網站後臺採用主流的 SSM 框架 jsp JSTL,網站後臺採用freemaker靜態化模版引擎生成html 2.因為是生成的html,所以訪問速度快,輕便,對服務 ...
  • 1..net ajax顯示後臺返回值 <script> $(document).ready(function () { $("#btn").click(function () { //var data = new string(); $.ajax({ type: "POST", //要用post方式 ...
  • 註意 轉載須保留原文鏈接(http://www.cnblogs.com/wzhiq896/p/6783296.html ) 作者:wangwen896 整理 1、分類 2、註釋方式 3、簡單指令 4、變數命名 5、NaN和isNaN 6、轉義字元 7、邏輯短路、邏輯中斷 8、優先順序 9、類型轉換(t ...
  • 最近接觸了Bootstrap,涉及到了LESS,CSS的預處理器使用最廣泛的就是LESS和Sass,都是努力把CSS武裝成為開發語言,讓它從簡單的描述性語言過渡到具有程式式特性的語言,主要的特性就是:變數、Mixins、嵌套、繼承等。就像教程里說的:CSS的預處理器就是讓CSS從設計師的工具,變為開 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...