无法使用 iOS 从主线程方法发送事件并对本机做出反应 [英] Can't send an event from main thread method with iOS and react native

查看:44
本文介绍了无法使用 iOS 从主线程方法发送事件并对本机做出反应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下课程:

#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

我的问题:我试图从主线程方法

My problem: I'm trying with no success to run updateLocationEvent method from main thread method

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations

其中(对于那些不熟悉它的人)是一种在发生重大变化时跟踪位置的方法.从 didUpdateLocations 调用时,一旦进入 updateLocationEvent 方法,属性 self.bridge 就是 nil!如果您查看其他方法 RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location) 我也从那里调用 updateLocationEvent 并发出事件就好了.实际上,我只是创建了一个方法来测试事件是否被触发,并且确实发生了.处理 didUpdateLocations 的类有一个 EventHandler 属性,它的初始化方式与我在objective-c 中看到的人们所做的相同:

Which (for those who are not familiar with it) is a method that tracks location whenever there's a significant change. Once inside the updateLocationEvent method while calling from didUpdateLocations the property self.bridge is nil! If you take a look at the other method RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location) I also call updateLocationEvent from there and it emits the event just fine. Actually I just created the method to test if the event was getting fired at all and it did. The class that handles didUpdateLocations has an EventHandler property which is initialized the same way I've seen people do in objective-c:

if (_eventHandler == nil) {
    _eventHandler = [[EventHandler alloc] init];
  }
  [_eventHandler updateLocationEvent];

也许我做错了?我对 obj-c 非常陌生,而且相对较新,但我还没有找到一个示例,我可以在其中从本机方法执行事件发射方法,因此我们将不胜感激.

Perhaps I'm doing this wrong? I'm very new to obj-c and relatively new to react but I just haven't found an example where I can execute an event emitting method from a native method so any help would be greatly appreciated.

推荐答案

问题不在于您从主线程调用,也不在于您的模块代码有任何问题.它不工作的原因是模块的桥属性尚未设置(为零).

The issue is not that you are calling from the main thread, nor is there anything wrong with your module code. The reason it's not working is that the bridge property of the module hasn't been set (it's nil).

真正的问题是您正在尝试使用 [[EventHandler alloc] init] 创建自己的模块实例,以便您可以调用 updateLocationEvent 方法,但这不是 React 中模块的使用方式.

The real problem is that you are trying to create your own instance of the module using [[EventHandler alloc] init] so that you can call the updateLocationEvent method, but that isn't how modules are used in React.

您永远不应该自己初始化 RCTBridgeModules(除非您通过 moduleProvider 块或 extraModules 委托方法将它们传递到桥中,但这可能与此处无关).模块在您创建时由桥自动实例化(通常在您的应用程序委托中),并且它们与桥的生命周期相关联.每个桥接实例在实例化模块时都会设置模块的 bridge 属性,因此如果您自己创建模块的新实例,它们的桥接属性将永远不会被设置.

You should never init RCTBridgeModules yourself (unless you are passing them into the bridge via the moduleProvider block or extraModules delegate method, but that's probably not relevant here). Modules are instantiated automatically by the bridge when you create it (typically in your app delegate), and they are tied to the bridge's lifecycle. Each bridge instance sets the bridge property of the modules when it instantiates them, so if you create new instances of the module yourself, their bridge property will never be set.

使用[bridge moduleForClass:] 方法从外部访问桥接模块实例是可能的(虽然很复杂),但我们使它变得困难的原因是因为这通常是一个坏主意.您不应该尝试在外部调用模块上的方法,因为您不知道桥接器是否已经完成加载 JS 代码,或者它是否在重新加载后被拆除.

It is possible (though complicated) to access bridge module instances externally by using the [bridge moduleForClass:] method, but the reason we've made it difficult is because it's generally a bad idea. You shouldn't try to call methods on your module externally because you don't know if the bridge has finished loading the JS code yet, or if it's in the process of being torn down after a reload.

将事件发送到 JS 的正确方法几乎总是从您的模块代码内部进行,以便桥接器可以管理您的代码的生命周期,并确保它只在一切设置好后才被调用正确启动.

The correct way to send events to JS is almost always do it from inside your module code, so that the bridge can manage the lifecycle of your code and make sure it's only called once everything is set up correctly.

Elliot 在上面说 RCTLocationObserver 已经提供了这个功能是正确的,所以我不确定你为什么需要复制它,但假设你有你的理由,理想情况下(如 Elliot 建议的那样)在上面的选项 2 中),您的 EventHandler 类将被设置为 CLLocationManager 的委托,并将实现 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations 内部.像这样:

Elliot is correct above when he says that RCTLocationObserver already provides this functionality, so I'm not exactly sure why you need to replicate it, but assuming you have your reasons, ideally (as Elliot suggests in his option 2 above), your EventHandler class would be set up as the delegate for CLLocationManager, and would implement the - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations internally. Something like this:

@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,以便它可以在您的应用程序中的适当时间调用.

This is basically how our own RCTLocationObserver works, although note that in this implementation the user will be prompted to enable location services immediately, whereas you may prefer to export the startUpdatingLocation method to JS so it can be called at the appropriate time in your application.

我不确定 Elliot 的选项 no 到底是什么意思.3 使用单例,因为这会遇到同样的问题,即桥不知道该实例,因此永远不会为您设置桥属性.我还建议一般不要使用模块的单例实例,因为它们会在桥重新加载 (cmd-R) 后保持其状态,这可能会导致奇怪的错误或意外行为.每次重新加载时,最好让桥拆除并重新创建所有模块,这是其默认行为.

I'm not sure exactly what Elliot meant with his option no. 3 of using a singleton, as this would suffer from the same problem that the bridge would be unaware of that instance, and so would never set the bridge property for you. I'd also advise against singleton instances of modules in general because they would retain their state after a bridge reload (cmd-R), which may lead to odd bugs or unexpected behaviour. It's better to let the bridge tear down and recreate all the modules every time it is reloaded, which is its default behaviour.

这篇关于无法使用 iOS 从主线程方法发送事件并对本机做出反应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆