JavaScriptCore не будет передавать строку как NSObject *

Рассмотрим следующий класс JSExported target-c со свойством NSObject *:

@protocol MyObjectExport <JSExport>
@property (nonatomic, strong) NSObject *myProperty;
@end

@interface MyObject : NSObject <MyObjectExport>
@end

@implementation MyObject
- (NSObject *)myProperty
{
    NSLog(@"in myProperty");
    return nullptr;
}
- (void)setMyProperty:(NSObject *)myProperty
{
    NSLog(@"in setMyProperty");
}
@end

Если я выполню этот код:

{
    JSContext *context = [[JSContext alloc] init];
    context[@"myObject"] = [[MyObject alloc] init];
    [context evaluateScript:@"var foo = myObject.myProperty; myObject.myProperty = foo;"];
}

Вызываются как myProperty, так и setMyProperty.

Но если я выполню этот код:

{
    JSContext *context = [[JSContext alloc] init];
    context[@"myObject"] = [[MyObject alloc] init];
    [context evaluateScript:@"myObject.myProperty = \"foo\";"];
}

setMyProperty не вызывается, предположительно, потому, что "foo" не совместим с NSObject *.

Однако, если я выполню этот код:

{
    MyObject *myObject = [[MyObject alloc] init];
    myObject.myProperty = @"foo";
}

setMyProperty вызывается.

Конечно, если я заменю NSObject * на NSString *, все будет работать нормально. Но мне нужно это свойство, чтобы иметь возможность хранить различные типы объектов.

Я не контролирую то, что передается в AssessmentScript. Однако есть ли способ, которым я могу кодировать вещи, чтобы JavaScriptCore универсально преобразовывал «foo» в NSString * перед передачей его обратному вызову target-c? Я понимаю, что могу перехватить вызов на стороне javascript и выполнить там необходимые действия, но я бы хотел избежать этого, если это возможно.


person Carl    schedule 15.04.2014    source источник


Ответы (1)


Если вам нужно сохранить один из многих типов, вы должны использовать id, а не NSObject *. Это сделает его общим указателем вроде void * в C.

Еще лучше вы могли бы использовать JSValue * в качестве типа, так как сообщения -[JSValue is*] могут помочь вам выяснить, какой тип был передан на самом деле. Недостатком этого является то, что ваши JSValue * свойства должны быть подкреплены JSManagedValue * иварами, чтобы предотвратить циклы сохранения. См. видео 615 WWDC 2013, примерно 2/3 пути, чтобы узнать больше об этой проблеме управления памятью.

person erm410    schedule 16.04.2014
comment
Ему не следует использовать JSValue в качестве свойства объекта target-c. Это вызовет циклы сохранения. Однако он мог бы использовать JSManagedValue. - person TechZen; 30.04.2014
comment
@TechZen Вы можете использовать JSValue как свойство, и на самом деле видео так и делает. Помните, что тип свойства — это просто тип, который принимает установщик и возвращает геттер, а не обязательно тип переменной, используемой для хранения свойства. Легко иметь свойство типа JSValue, которое обращается к переменным типам JSManagedValue. На это и было направлено мое предложение. - person erm410; 30.04.2014
comment
Ах, да, вы могли бы, хотя это кажется хорошим способом прострелить вам ногу. - person TechZen; 30.04.2014
comment
@TechZen Я с уважением не согласен. Идея объектно-ориентированного программирования состоит в том, чтобы отделить интерфейс от реализации посредством инкапсуляции. JSManagedValue был введен для решения проблемы хранения, но пользователи вашего класса не заботятся (и не должны) о том, как вы храните свои JSValues ​​(через JSManagedValue ivar, реализацию), а только о том, как получить к ним доступ (через свойство JSValue, интерфейс). - person erm410; 30.04.2014