一、JavaScriptCore常用的類
-
JavaScriptCore作用:JavaScriptCore是蘋果原生API,用來JS和OC交互的。 -
JSContext: JS運(yùn)行環(huán)境,用它去執(zhí)行JS代碼,并且通過它去獲取JS里的數(shù)據(jù)。 -
JSValue: 用于接收J(rèn)S中獲取的數(shù)據(jù)類型,可以是任一對象,方法。
二、OC調(diào)用JS
- 本質(zhì):JS代碼中已經(jīng)定義好變量和方法,通過OC去獲取,并且調(diào)用
- 步驟:
- 創(chuàng)建JS運(yùn)行環(huán)境
- 執(zhí)行JS代碼
- 獲取JS數(shù)據(jù)(變量,方法)
- 使用JS數(shù)據(jù),方法
2.1 獲取定義在JS中的變量
- 可以直接通過OC修改JS中變量的值
#pragma mark - 獲取JS中定義的變量
- (void)getJSVar {
// JS代碼
NSString *jsCode = @"var arr = [1,2,3]";
// 創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];
// 執(zhí)行JS代碼
[ctx evaluateScript:jsCode];
// 因?yàn)樽兞恐苯佣x在JS中,所以可以直接通過JSContext獲取,根據(jù)變量名稱獲取,相當(dāng)于字典的Key
// 只有先執(zhí)行JS代碼,才能獲取變量
JSValue *jsArr = ctx[@"arr"];
jsArr[0] = @5;
// 打印結(jié)果:5,2,3
NSLog(@"%@",jsArr);
}
2.2 獲取定義在JS中的方法,并且調(diào)用
- 實(shí)現(xiàn)OC調(diào)用JS方法
#pragma mark - OC調(diào)用JS
// OC調(diào)用JS方法,并獲取返回結(jié)果
- (void)ocCallJSFunc {
NSString *jsCode = @"function hello(say){
return say;
}";
// 創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];
// 因?yàn)榉椒ㄖ苯佣x在JS中,所以可以直接通過JSContext獲取,根據(jù)方法名稱獲取,相當(dāng)于字典的Key
// 執(zhí)行JS代碼
[ctx evaluateScript:jsCode];
// 獲取JS方法,只有先執(zhí)行JS代碼,才能獲取
JSValue *hello = ctx[@"hello"];
// OC調(diào)用JS方法,獲取方法返回值
JSValue *result = [hello callWithArguments:@[@"你好"]];
// 打印結(jié)果:你好
NSLog(@"%@",result);
}
三、JS調(diào)用OC中的block
- 本質(zhì):一開始JS中并沒有OC的block,所以沒法直接調(diào)用OC的block,需要把OC的block,在JS中生成方法,然后在通過JS調(diào)用。
- 步驟:
- 創(chuàng)建JS運(yùn)行環(huán)境
- 在JS中生成對應(yīng)的OC代碼
- 使用JS調(diào)用,在JS環(huán)境中生成的block方法,就能調(diào)用到OC的block中.
3.1 JS調(diào)用OC中不帶參數(shù)的block
- 想通過JS調(diào)用OC中不帶參數(shù)的block
#pragma mark - JS調(diào)用OC中不帶參數(shù)的block
- (void)jsCallOCBlock1WithNoneArguments {
// 創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];
// JS調(diào)用Block方式
// 由于JS本身沒有OC這個(gè)代碼,需要給JS中賦值,就會(huì)自動(dòng)生成右邊的代碼.
// 相當(dāng)于在JS中定義一個(gè)叫eat的方法,eat的實(shí)現(xiàn)就是block中的實(shí)現(xiàn),只要調(diào)用eat,就會(huì)調(diào)用block
ctx[@"eat"] = ^(){
NSLog(@"吃東西");
};
// JS執(zhí)行代碼,就會(huì)直接調(diào)用到block中
NSString *jsCode = @"eat()";
[ctx evaluateScript:jsCode];
// 打印結(jié)果:吃東西
}
3.2 JS調(diào)用OC中帶參數(shù)的block
- 想通過JS調(diào)用OC中帶參數(shù)的block
- (void)jsCallOCBlockWithArguments {
// 創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];
// 2.調(diào)用帶有參數(shù)的block
// 還是一樣的寫法,會(huì)在JS中生成eat方法,只不過通過[JSContext currentArguments]獲取JS執(zhí)行方法時(shí)的參數(shù)
ctx[@"eat"] = ^(){
// 獲取JS調(diào)用參數(shù)
NSArray *arguments = [JSContext currentArguments];
NSLog(@"吃%@",arguments[0]);
};
// JS執(zhí)行代碼,調(diào)用eat方法,并傳入?yún)?shù)面包
NSString *jsCode = @"eat('面包')";
[ctx evaluateScript:jsCode];
}
四、JS調(diào)用OC中的類
本質(zhì):一開始JS中并沒有OC的類,需要先在JS中生成OC的類,然后在通過JS調(diào)用。
-
步驟
- OC類必須遵守JSExport協(xié)議,只要遵守JSExport協(xié)議,JS才會(huì)生成這個(gè)類
- 但是還不夠,類里面有屬性和方法,也要在JS中生成
- JSExport本身不自帶屬性和方法,需要自定義一個(gè)協(xié)議,繼承JSExport,在自己的協(xié)議中暴露需要在JS中用到的屬性和方法
- 這樣自己的類只要繼承自己的協(xié)議就好,JS就會(huì)自動(dòng)生成類,包括自己協(xié)議中聲明的屬性和方法
4.1 JS調(diào)用OC自定義類
- 自定義協(xié)議(PersonJSExport)
@protocol PersonJSExport <JSExport>
@property (nonatomic, strong) NSString *name;
- (void)play;
// 調(diào)用多個(gè)參數(shù)的方法,JS函數(shù)命名規(guī)則和OC還不一樣,很可能調(diào)用不到對應(yīng)的JS生成的函數(shù),
// 為了保證生成的JS函數(shù)和OC方法名一致,OC提供了一個(gè)宏JSExportAs,
// 用來告訴JS應(yīng)該生成什么樣的函數(shù)對應(yīng)OC的方法,這樣就不會(huì)調(diào)錯(cuò)了。
// PropertyName:JS函數(shù)生成的名字
// Selector:OC方法名
// JS就會(huì)自動(dòng)生成playGame這個(gè)方法
JSExportAs(playGame, - (void)playWithGame:(NSString *)game time:(NSString *)time); @end
- 自定義類(Person)
@interface Person : NSObject <PersonJSExport>
@property (nonatomic, strong) NSString *name;
- (void)playWithGame:(NSString *)game time:(NSString *)time;
@end
@implementation Person
- (void)play {
NSLog(@"%@玩",_name);
}
- (void)playWithGame:(NSString *)game time:(NSString *)time {
NSLog(@"%@在%@玩%@",_name,time,game);
}
@end
- 通過JS調(diào)用OC自定義類
#pragma mark - JS調(diào)用OC自定義類
- (void)jsCallOCCustomClass {
// 創(chuàng)建Person對象
Person *p = [[Person alloc] init];
p.name = @"yz";
JSContext *ctx = [[JSContext alloc] init];
// 會(huì)在JS中生成Person對象,并且擁有所有值
// 前提:Person對象必須遵守JSExport協(xié)議,
ctx[@"person"] = p;
// 執(zhí)行JS代碼
// 注意:這里的person一定要跟上面聲明的一樣,因?yàn)樯傻膶ο笫怯胮erson引用
// NSString *jsCode = @"person.play()";
NSString *jsCode = @"person.playGame('德州撲克','晚上')";
[ctx evaluateScript:jsCode];
}
4.1 JS調(diào)用OC系統(tǒng)類
-
問題:系統(tǒng)自帶的類,想要通過JS調(diào)用怎么辦,我們又沒辦法修改系統(tǒng)自帶類的文件
- 和調(diào)用自定義類一樣,也要弄個(gè)自定義協(xié)議繼承JSExport,描述需要暴露哪些屬性(想要把系統(tǒng)類的哪些屬性暴露,就在自己的協(xié)議聲明)
- 通過runtime,給類添加協(xié)議
自定義協(xié)議(UILabelJSExport)
@protocol UILabelJSExport <JSExport>
@property (nonatomic, strong) NSString *text;
@end
- JS調(diào)用OC系統(tǒng)類
#pragma mark - JS調(diào)用OC系統(tǒng)類
- (void)jsCallOCSystemClass {
// 給系統(tǒng)類添加協(xié)議
class_addProtocol([UILabel class], @protocol(UILabelJSExport));
// 創(chuàng)建UILabel
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
[self.view addSubview:label];
JSContext *ctx = [[JSContext alloc] init];
// 就會(huì)在JS中生成label對象,并且用laebl引用
ctx[@"label"] = label;
// 利用JS給label設(shè)置文本內(nèi)容
NSString *jsCode = @"label.text = 'Oh Year'";
[ctx evaluateScript:jsCode];
}