JavaScriptCore 에서 JSpatch, ReactNative 까지
JavaScriptCore
JS 는 OC 처럼 실행 되 는 과정 에서 자신의 역할 영역 이 있 습 니 다. 이 역할 영역 은 모든 역할 영역 내 변수 와 함 수 를 가 져 올 수 있 습 니 다. JavaScriptCore 는 JS 코드 를 실행 하 는 '역할 영역' 을 제공 합 니 다.
JSContext,JSValue
OC call JS
JSContext *jsContext = [[JSContext alloc] init];
//define var
[jsContext evaluateScript:@"var jsCode = 2"];
JSValue *jscode = jsContext[@"jsCode"];
//define method
[jsContext evaluateScript:@"function min(a,b) { if (a > b) {return b;} return a}"];
//way 1
[jsContext evaluateScript:@"min(5,4)"];
//way 2
JSValue *min = jsContext[@"min"];
[min callWithArguments:@[@5,@4]];
OC 는 '역할 영역' JScontext 를 만들어 서 독립 된 JS 실행 환경, 실행 방법 과 변수 정의 코드 를 구축 하고 아래 표 시 된 방식 으로 방법 과 변 수 를 가 져 올 수 있 으 며 호출 방법 에서 문자열 문 구 를 실행 하거나 JSValue 의 call 방법 을 선택 할 수 있 습 니 다.
JS call OC
JS 의 코드 는 항상 OC 가 만 든 JScontext 로 실행 되 어야 하기 때문에 OC 는 JS 코드 의 생김새 를 명확 하 게 호출 하면 직접 호출 할 수 있 습 니 다. 반대로 JS 는 OC 코드 의 구체 적 인 생김새 를 알 수 없고 알 아 도 명확 하 게 호출 할 수 없 기 때문에 애플 은 OC 의 block 을 JSValue 로 전환 하여 JScontext 에 등록 할 수 있 습 니 다.그리고 JS 코드 는 변경 block 을 가 져 오고 호출 할 수 있 습 니 다.
JSContext *jsContext = [[JSContext alloc] init];
jsContext[@"ocMin"]= ^(int a, int b) {
if (a > b) {
return b;
}
return a;
};
[jsContext evaluateScript:@"ocMin(5, 4)"];
JSExport
@protocol JSExport
@end
#define JSExportAs(PropertyName, Selector) \
@optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif
#import
#import
@protocol JSExportProtocol
- (void)text;
@end
@interface JSExportText : NSObject
- (void)text;
@end
@implementation JSExportText
- (void)text
{
NSLog(@"text");
}
@end
//test code
JSContext *jsContext = [[JSContext alloc] init];
// jsContext , JSExportText
jsContext[@"JSExportText"] = [JSExportText class];
[jsContext evaluateScript:@"var jsExportText = JSExportText.getInstance(); jsExportText.text()"];
JSexportProtocol 은 JSexport 를 따 릅 니 다. 그러면 JavaScriptCore 는 이러한 방법 과 속성 을 자동 으로 설정 하여 JS 호출 이 현재 의 방법 을 얻 을 수 있 도록 합 니 다.
JSPatch
jsPatch 는 현재 iOS 에서 가장 자주 사용 되 는 열 업데이트 방법 으로 순수 Swift 코드 는 지원 되 지 않 지만.
프로 세 스
1. JS 코드 (재 작성 방법 과 속성 을 정의 함) - > 2. 전 환 된 JS 코드 - > 3. 방법 교체 메모리 - > 4. 방법 호출 전송 찾기 이 네 단 계 는 JSpatch 의 전체 과정 입 니 다. 우 리 는 하나의 예 를 통 해 전체 과정 을 설명 합 니 다.
defineClass("JPTableViewController", {
viewDidLoad: function() {
self.ORIGviewDidLoad();
},
})
이상 은 공식 적 인 예 입 니 다. 목적 은 JPTable ViewController 의 view DidLoad 를 다시 쓰 는 것 입 니 다. 상기 코드 는 사실은 defineClass 함 수 를 호출 한 것 입 니 다. 첫 번 째 매개 변 수 는 클래스 이름, 두 번 째 배열 입 니 다. 인 스 턴 스 방법 배열 입 니 다. 세 번 째 매개 변 수 는 추가 되 지 않 았 습 니 다. 추가 하면 클래스 방법 배열 입 니 다. 이런 표기 법 은 매우 명확 합 니 다. 지난 절 에서 JS 는 context 가 실행 되 어야 한 다 는 것 을 알 고 있 습 니 다.그러나 위의 코드 에 있 는 defineClass 라 는 함 수 는 정의 되 지 않 았 습 니 다. 어떻게 실행 합 니까?사실 JSpatch 는 코드 를 실행 하기 전에 많은 작업 을 했 습 니 다. 다음은 JSpatch 의 초기 화 부분 코드 입 니 다. 중간 에 많이 생략 되 었 습 니 다.
[JPEngine startEngine]; //
[JPEngine evaluateScript:string]; //
//
+ (void)startEngine
{
if (_context) {
return;
}
JSContext *context = [[JSContext alloc] init];
context[@"_OC_defineClass"] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) {
return defineClass(classDeclaration, instanceMethods, classMethods);
};
////
context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
NSLog(@"%@", exception);
NSAssert(NO, @"js exception: %@", exception);
};
_context = context;
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"WatchDog" ofType:@"js"];
NSAssert(path, @"can't find JSPatch.js");
NSString *jsCore = [[NSString alloc] initWithData:[[NSFileManager defaultManager] contentsAtPath:path] encoding:NSUTF8StringEncoding];
[_context evaluateScript:jsCore];
}
위 에서 보 듯 이 JSpatch 는 context 를 가지 고 js 코드 를 실행 하지만 defineClass 의 정 의 를 보지 못 했 습 니 다. 우 리 는 상단 코드 의 마지막 부분 [ context evaluate Script: jsCore] 을 유의 합 니 다.이 코드 는 JSpatch. js 에서 실 행 된 코드 입 니 다. JSpatch. js 코드 를 캡 처 합 니 다.
var global = this
global.defineClass = function(declaration, instMethods, clsMethods) {
var newInstMethods = {}, newClsMethods = {}
_formatDefineMethods(instMethods, newInstMethods)
_formatDefineMethods(clsMethods, newClsMethods)
var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods)
return require(ret["cls"])
}
ok, 양 끝 코드 결합 defineclass - >OC_defineClass - > defineClass () (oc 내부 코드 defineclass) 두 번 째 단 계 는 js 방법 을 내부 방법 으로 정의 하 는 방법 입 니 다.
static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
{
NSString *className;
NSString *superClassName;
NSString *protocolNames;
NSScanner *scanner = [NSScanner scannerWithString:classDeclaration];
[scanner scanUpToString:@":" intoString:&className];
if (!scanner.isAtEnd) {
scanner.scanLocation = scanner.scanLocation + 1;
[scanner scanUpToString:@"" intoString:&protocolNames];
}
}
NSArray *protocols = [protocolNames componentsSeparatedByString:@","];
if (!superClassName) superClassName = @"NSObject";
className = trim(className);
superClassName = trim(superClassName);
Class cls = NSClassFromString(className);
if (!cls) {
Class superCls = NSClassFromString(superClassName);
cls = objc_allocateClassPair(superCls, className.UTF8String, 0);
objc_registerClassPair(cls);
}
//
for (int i = 0; i < 2; i ++) {
BOOL isInstance = i == 0;
JSValue *jsMethods = isInstance ? instanceMethods: classMethods;
Class currCls = isInstance ? cls: objc_getMetaClass(className.UTF8String);
NSDictionary *methodDict = [jsMethods toDictionary];
for (NSString *jsMethodName in methodDict.allKeys) {
if ([jsMethodName isEqualToString:@"__c"]) {
continue;
}
JSValue *jsMethodArr = [jsMethods valueForProperty:jsMethodName];
int numberOfArg = [jsMethodArr[0] toInt32];
NSString *tmpJSMethodName = [jsMethodName stringByReplacingOccurrencesOfString:@"__" withString:@"-"];
NSString *selectorName = [tmpJSMethodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
selectorName = [selectorName stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
if (!countArgRegex) {
countArgRegex = [NSRegularExpression regularExpressionWithPattern:@":" options:NSRegularExpressionCaseInsensitive error:nil];
}
NSUInteger numberOfMatches = [countArgRegex numberOfMatchesInString:selectorName options:0 range:NSMakeRange(0, [selectorName length])];
if (numberOfMatches < numberOfArg) {
selectorName = [selectorName stringByAppendingString:@":"];
}
JSValue *jsMethod = jsMethodArr[1];
if (class_respondsToSelector(currCls, NSSelectorFromString(selectorName))) {
overrideMethod(currCls, selectorName, jsMethod, !isInstance, NULL);
} else {
BOOL overrided = NO;
for (NSString *protocolName in protocols) {
char *types = methodTypesInProtocol(protocolName, selectorName, isInstance, YES);
if (!types) types = methodTypesInProtocol(protocolName, selectorName, isInstance, NO);
if (types) {
overrideMethod(currCls, selectorName, jsMethod, !isInstance, types);
overrided = YES;
break;
}
}
if (!overrided) {
NSMutableString *typeDescStr = [@"@@:" mutableCopy];
for (int i = 0; i < numberOfArg; i ++) {
[typeDescStr appendString:@"@"];
}
overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:NSUTF8StringEncoding]);
}
}
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
class_addMethod(cls, @selector(getProp:), (IMP)getPropIMP, "@@:@");
class_addMethod(cls, @selector(setProp:forKey:), (IMP)setPropIMP, "v@:@@");
#pragma clang diagnostic pop
return @{@"cls": className};
}
\\
static void overrideMethod(Class cls, NSString *selectorName, JSValue *function, BOOL isClassMethod, const char *typeDescription)
{
//
SEL selector = NSSelectorFromString(selectorName);
NSMethodSignature *methodSignature;
IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL;
//
IMP msgForwardIMP = _objc_msgForward;
class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
//
if (class_getMethodImplementation(cls, @selector(forwardInvocation:)) != (IMP)JPForwardInvocation) {
IMP originalForwardImp = class_replaceMethod(cls, @selector(forwardInvocation:), (IMP)JPForwardInvocation, "v@:@");
class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
}
//
if (class_respondsToSelector(cls, selector)) {
NSString *originalSelectorName = [NSString stringWithFormat:@"ORIG%@", selectorName];
SEL originalSelector = NSSelectorFromString(originalSelectorName);
if(!class_respondsToSelector(cls, originalSelector)) {
class_addMethod(cls, originalSelector, originalImp, typeDescription);
}
}
NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@", selectorName];
SEL JPSelector = NSSelectorFromString(JPSelectorName);
NSString *clsName = NSStringFromClass(cls);
// js
if (!_JSOverideMethods[clsName][JPSelectorName]) {
_initJPOverideMethods(clsName);
_JSOverideMethods[clsName][JPSelectorName] = function;
class_addMethod(cls, JPSelector, msgForwardIMP, typeDescription);
}
}
위의 일부 코드 를 간소화 하 였 다.이 과정 은 다음 과 같다.objc_msg Forward, 이렇게 모든 호출 방법 이 호출 됩 니 다objc_msg Forward, 이 럴 때 forward Invocation 2 를 호출 합 니 다. forward Invocation 을 JP Forward Invocation 으로 바 꿉 니 다. 이 방법 은 마지막 으로 JP Forward Invocation 3 을 호출 합 니 다. 원래 의 방법 을 ORIG 로 시작 하 는 방법 으로 바 꾸 면 원래 의 방법 을 찾 을 수 있 습 니 다. 4. 실제 JS 의 코드 function 을 사전 에 저장 합 니 다. 사전 은...클래스 이름 은 key, 값 은 사전 입 니 다. 이 사전 은 방법 은 key, 값 은 js 의 function 형식, jSVValue 를 저장 합 니 다.
전체 과정 정리 1. 초기 화 과정 definclass (개발 자가 직접 쓴 js 코드 호출 defineclass) - > definclass (세 개의 매개 변수, 유형, 방법, 클래스 방법 으로 전환) - >OC_defineClass (js function 을 전역 사전 에 저장 하고 원래 방법 을 저장 하 며 원래 방법 을 방법 으로 호출 하여 전송) 2. 실행 과정 callMethod (OC 의 방법) - > JP Forward Invocation (매개 변 수 를 초기 화하 고 js function 을 찾 습 니 다) - > JSfunction (실행)
전체 과정 을 JSpatch 가 이 용 했 습 니 다.
계속
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.