iOS7이전에는 자바스크립트와 무언가 작용을 하려면 UIWebView의 stringByEvaluatingJavaScriptFormString:를 사용해야 했다. 이는 웹뷰의 자바스크립트 런타임에서만 가능했다. 이는 싱글스레드인 환경의 제약이 있다.
JavaScriptCore는 Objective-C에서 자바스크립트의 풀런타임환경에 접근이 가능하게 한다. 문법확인, 스크립트실행과 변수 접근, 콜백수신 그리고 Objective-C 객체의 공유등으로 광범위한 상호작용이 가능하다.
JSVirtualMachine클래스로 불리는 가상머신에서 실행된다. 이는 여러개의 VM을 통해 멀티스레드 자바스크립팅이 가능하다는 것이다. JSVirtualMachine 각각은 여러개의 JSContext를 가질 수 있다. JSContext는 자바스크립트 런타입환경에 말하고 몇가지 기능을 제공한다. 이 중 두가지는 글로벌객체에 접근하는것과 스크립트를 실행할 수 있다는 것이다.
글로벌 영역에 정의된 변수에 대한 접근은 키로 바로 접근이 가능하다.
JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
context[@"a"] = @5;
JSValue *aValue = context[@"a"];
double a = [aValue toDouble];
NSLog(@"%.0f", a);
함수호출은 다음과 같다
[context evaluateScript:@”var square = function(x) {return x*x;}”];
JSValue *squareFunction = context[@”square”];
NSLog(@”%@”, squareFunction);
JSValue *aSquared = [squareFunction callWithArguments:@[context[@”a”]]];
NSLog(@”a^2: %@”, aSquared);
JSValue *nineSquared = [squareFunction callWithArguments:@[@9]];
NSLog(@”9^2: %@”, nineSquared);
evaluateScript로 스크립트 코드를 로드하고 callWithArguments로 스크립트내의 함수를 호출한다. 호출된 결과는 JSValue로 반환된다.
스크립트코드는 블럭코드형태로도 가능하다.
context[@"factorial"] = ^(int x) {
int factorial = 1;
for (; x > 1; x--) {
factorial *= x;
}
return factorial;
};
[context evaluateScript:@"var fiveFactorial = factorial(5);"];
JSValue *fiveFactorial = context[@"fiveFactorial"];
NSLog(@"5! = %@", fiveFactorial);
// Objective-C type | JavaScript type
// --------------------+---------------------
// nil | undefined
// NSNull | null
// NSString | string
// NSNumber | number, boolean
// NSDictionary | Object object
// NSArray | Array object
// NSDate | Date object
// NSBlock | Function object
// id | Wrapper object
// Class | Constructor object
스크립트NSNumber와 NSBlocks 타입은 mutable이 아니다 즉 한쪽에서 변경했다고 다른쪽에서 반영이 되지 않는다. id와 Class는 mutable이다. 임의 오브젝트나 클래스가 자바스크립트에 브릿지될 수 있다. NSNull, NSString, NSNumber, NSDictionary, NSArray 또는 NSDate는 관련 클래스 계층을 JavaScript 실행 컨텍스트로 가져 와서 동등한 클래스 및 프로토 타입을 생성하는 JavaScriptCore를 생성하려고 한다.
JSExport protocol로 자바스크립트에 커스텀클래스의 일부를 노출할 수 있다. 래퍼객체는 다른곳에 생성될것이다. 그리하여 한개의 객체가 양쪽의 실행컨텍스트에 공유될 수 있다.
다음이 그 예이다.
@protocol ThingJSExports <JSExport>
@property (nonatomic, copy) NSString *name;
@end
@interface Thing : NSObject <ThingJSExports>
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger number;
@end
@implementation Thing
- (NSString *)description {
return [NSString stringWithFormat:@"%@: %d", self.name, self.number];
}
@end
JSExport를 상속하여 ThingJSExports를 선언함으로써 자바스크립트에 노출할 수 있다. 이제 objective-c객체의 정보를 변경하면 자바스크립트에 그 값이 반영이 될것이다. 그 반대로 마찬가지.다음은 id와 class를 브릿지한 예이다.
다음은 id와 class를 브릿지한 예이다.
Thing *thing = [[Thing alloc] init];
thing.name = @"Alfred";
thing.number = 3;
context[@"thing"] = thing;
JSValue *thingValue = context[@"thing"];
context[@"thing"] = [Thing class];
NSString *thingScript =
@"var t = new thing();"
"t.name = 'yslee';"
"t.number = 10;"
[context eveluateScript:thingScript];