我有以下课程:
#import "EventHandler.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import <pthread.h>
@implementation EventHandler
RCT_EXPORT_MODULE();
@synthesize bridge = _bridge;
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
mach_port_t machTID = pthread_mach_thread_np(pthread_self());
NSLog(@"Pretending to create an event %@ at %@, current thread: %x", name, location, machTID);
[self updateLocationEvent];
}
- (void)updateLocationEvent
{
NSString *eventName = @"name!!!";
mach_port_t machTID = pthread_mach_thread_np(pthread_self());
NSLog(@"about to submit event, current thread: %x",machTID);
[self.bridge.eventDispatcher sendAppEventWithName:@"UpdateLocation"
body:@{@"name": eventName}];
}
@end
我的问题:我尝试从主线程方法运行updateLocationEvent
方法没有成功
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
对于那些不熟悉它的人来说,这是一种在发生重大变化时跟踪位置的方法。进入updateLocationEvent
方法,在didUpdateLocations
来电时,属性self.bridge
为nil
!如果您看一下其他方法RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
,我也会从那里调用updateLocationEvent
,它就会很好地发出事件。实际上我刚刚创建了测试事件是否被触发的方法,并且确实已经触发了。处理didUpdateLocations
的类具有EventHandler
属性,该属性的初始化方式与我在people-c中看到的人一样:
if (_eventHandler == nil) {
_eventHandler = [[EventHandler alloc] init];
}
[_eventHandler updateLocationEvent];
也许我这样做错了?我对obj-c非常新,而且相对较新,但我还没有找到一个例子,我可以从本机方法执行事件发射方法,所以任何帮助都会非常感激。
答案 0 :(得分:1)
问题在于,在此代码中创建EventHandler
实例
if (_eventHandler == nil) {
_eventHandler = [[EventHandler alloc] init];
}
[_eventHandler updateLocationEvent];
_eventHandler.bridge
未设置。它通常在React Native网桥初始化期间自动设置。
这里有几个选项:
RCTLocationObserver
。EventHandler
设为CLLocationManagerDelegate
而不是其他类。查看RCTLocationObserver
做什么,做类似的事情。EventHandler
成为单身人士,并在其他代码中使用该实例。如果可能,我可能会做#1。如果没有,#2。如果您决定执行#3,则需要在.m文件中添加类似的代码:
static EventHandler *instance = nil;
+ (EventHandler *)getInstance {
if (instance == nil) {
instance = [[EventHandler alloc] init];
}
return instance;
}
然后使用[EventHandler getInstance]
创建EventHandler
的实例,将其导出为桥接模块。您还可以在didUpdateLocations
[[EventHandler getInstance] updateLocationEvent];
答案 1 :(得分:1)
问题不在于您是从主线程调用,也不是您的模块代码有任何问题。它不工作的原因是模块的桥属性没有设置(它没有)。
真正的问题是您正在尝试使用[[EventHandler alloc] init]
创建自己的模块实例,以便您可以调用updateLocationEvent
方法,但这不是模块的使用方式反应。
您永远不应该自己初始化RCTBridgeModules(除非您通过moduleProvider块或extraModules
委托方法将它们传递到桥中,但这可能与此无关)。当您创建模块时,模块会由网桥自动实例化(通常在您的应用委托中),并且它们与网桥的生命周期相关联。每个桥实例在实例化时都会设置模块的bridge
属性,因此如果您自己创建模块的新实例,则永远不会设置它们的桥属性。
使用[bridge moduleForClass:]
方法从外部访问桥接模块实例是可能的(尽管很复杂),但我们之所以难以理解是因为它通常是一个坏主意。您不应该尝试在外部调用模块上的方法,因为您还不知道桥接器是否已完成加载JS代码,或者它是否在重新加载后被拆除的过程中
将事件发送到JS的正确方法几乎总是从 inside 模块代码中执行,以便桥接器可以管理代码的生命周期并确保它只被调用一切都设置正确。
当他说RCTLocationObserver
已经提供此功能时,艾略特是正确的,所以我不确定你为什么需要复制它,但是假设你有理由,理想情况下(正如艾略特所建议的那样)他的上述选项2),您的EventHandler
课程将设置为CLLocationManager
的代理人,并在内部实施- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
。像这样:
@interface EventHandler () <CLLocationManagerDelegate>
@end
@implementation EventHandler
{
CLLocationManager *_locationManager;
}
RCT_EXPORT_MODULE();
@synthesize bridge = _bridge;
- (instancetype)init
{
if ((self = [super init])) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager startUpdatingLocation];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSString *eventName = @"name!!!";
[self.bridge.eventDispatcher sendAppEventWithName:@"UpdateLocation"
body:@{@"name": eventName}];
}
@end
这基本上是我们自己的RCTLocationObserver
的工作方式,但请注意,在此实现中,系统会提示用户立即启用位置服务,而您可能更愿意将startUpdatingLocation
方法导出到JS,以便它可以在您的申请中的适当时间调用。
我不确定Elliot的选择是什么意思。 3使用单例,因为这会遇到桥不会意识到该实例的相同问题,因此永远不会为您设置桥属性。我还建议一般模块的单例实例,因为它们会在桥重载(cmd-R)后保留其状态,这可能导致奇怪的错误或意外行为。最好让桥每次重新加载时拆除并重新创建所有模块,这是它的默认行为。