【问题标题】:Share singleton from Objective C to Swift将单例从 Objective C 共享到 Swift
【发布时间】:2020-08-20 21:06:02
【问题描述】:

我正在尝试从 Swift 访问一个 Objective C 单例,但是我似乎只获得了在单例的 init 函数中创建的初始值。暴露的 flightControllerState 对象在委托函数中更新,我可以看到该值在 Objective C 端正确更新。

我在 SO 和 article 上关注了一些不同的帖子,了解如何从 Swift 调用共享对象。 (我还应该提到这是在 react native 项目中运行,如果这可能会产生任何影响?)

编辑更新了 swift 代码 - 我在 init 方法中添加了错误的行以获取共享实例 - 问题仍然相同

Objective-C 单例

@import DJISDK;

@interface RCTBridgeDJIFlightController : RCTEventEmitter<DJIFlightControllerDelegate> {
    DJIFlightControllerState *flightControllerState;
}


@property(nonatomic, readonly) DJIFlightControllerState *flightControllerState;

+ (id)sharedFlightController;

@end


@implementation RCTBridgeDJIFlightController

DJIFlightControllerState *flightControllerState;

@synthesize flightControllerState;

    + (id)sharedFlightController {
        static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedFlightControllerInstance = [[self alloc] init];
        });
        return sharedFlightControllerInstance;
    }

    - (id)init {
    // I also tried this to make sure the shared instance was returned but no luck
    //if (sharedFlightControllerInstance != nil) {
    //    return sharedFlightControllerInstance;
    //}
    if (self = [super init]) {
        flightControllerState = nil;
    }
    return self;
    }


    -(void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
    flightControllerState = state;
    }
@end

Swift 类调用单例并访问值

class VirtualStickController {
  var flightControllerSharedInstance: RCTBridgeDJIFlightController

  override init() {
      self.flightControllerSharedInstance = RCTBridgeDJIFlightController.sharedFlightController()
  }

  func getFlightControllerState() {
      if let state = flightControllerSharedInstance.flightControllerState {
      print("FLIGHT CONTROLLER STATE: \(state)") // always null
    } else {
      print ("NULL")
    }
  }

【问题讨论】:

  • 如果你的类中有一个 init 并在 swift override init 中分配它,那么你的代码中没有使用单例,而是一个普通的实例类型。

标签: ios objective-c swift react-native


【解决方案1】:
DJIFlightControllerState *flightControllerState;

@synthesize flightControllerState;

除特殊情况外,(现代)Objective-C 中的属性无需使用@synthesize

属性flightControllerState 是一个实例属性,将使用隐藏的instance 变量来合成(带或不带@synthesize)用于存储。

变量flightControllerState是一个全局变量,它恰好与属性同名,但与它没有任何关系。

猜测您正在更改 Objective-C 中的 全局变量,并期望通过 property 在 Swift 中看到结果,但您不会。

删除全局变量,然后检查其余代码。

除此之外,您的代码会生成一个有效的共享实例,该实例可以在 Objective-C 和 Swift 之间共享,并且以一种语言所做的更改将在另一种语言中可见。

HTH

【讨论】:

  • 感谢您的详细解释!是的,这正是我所期望看到的,我肯定混淆了全局变量和属性。我通过调整我的 init 方法来解决这个问题,使其始终返回 sharedFlightControllerInstance ,除非它不存在。不确定这是否是好的做法?
  • - (id)init { if (sharedFlightControllerInstance) { return sharedFlightControllerInstance; } //确保只有一个实例存在 @synchronized(self) { self = [super init]; if (self) { sharedFlightControllerInstance = self; } 返回自我; } }
  • 关于编写真正单例的一种方法,请参阅this answer,它基于 Apple 在过去时代记录的相同模型 ;-)
【解决方案2】:

关于如何从 Swift 访问 Objective C 单例的名义问题,我会推荐一个替代方案。现代惯例是将您的sharedFlightController 声明为class 属性并将init 声明为NS_UNAVAILABLE

@interface RCTBridgeDJIFlightController : NSObject
...
@property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
@end

该实现将为此类属性实现一个 getter:

@implementation RCTBridgeDJIFlightController

+ (instancetype)sharedFlightController {
    static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedFlightControllerInstance = [[self alloc] init];
    });
    return sharedFlightControllerInstance;
}

...

@end

现在,您的 Swift 代码可以引用 RCTBridgeDJIFlightController.shared,这与 Swift 单例的约定一样。


关于您收到nil 状态的原因,可能是以下两个问题之一:

  • 您的 Objective-C 代码将明确定义的 ivars、手动综合和全局变量的组合混淆了。 (见下文。)

  • 我还建议您确认 flightController:didUpdateState: 是否曾经被调用过。 (我没有看到你设置过飞行控制器的委托。)在该方法中添加断点或NSLog 语句并确认。

关于上面的第一个问题,我建议:

  1. 您不应该在 init 方法中使用这些注释行。如果您想确保使用您的单例对象,请将init 声明为NS_UNAVAILABLE

  2. 鉴于您的所有init 方法正在将flightControllerState 更新为nil,您可以将其完全删除。在 ARC 中,属性会为您初始化为 nil

  3. 您不应在 @interface 中声明显式 ivar。让编译器自动为你合成。

  4. 你不应该@synthesize @implementation 中的 ivar。编译器现在将自动为您合成(并将为 ivar 使用适当的名称,在属性名称中添加下划线。

  5. 您不应在 @implementation 中声明该全局变量。

  6. 如果你想在 Swift 中使用这个 sharedFlightController,你应该将它定义为一个 class 属性,而不是一个类方法。我知道that article 建议使用类方法,但这确实不是最佳实践。

因此:

// RCTBridgeDJIFlightController.h

#import <Foundation/Foundation.h>

// dji imports here

NS_ASSUME_NONNULL_BEGIN

@interface RCTBridgeDJIFlightController : NSObject
@property (nonatomic, readonly, nullable) DJIFlightControllerState *flightControllerState;
@property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
@end

NS_ASSUME_NONNULL_END

// RCTBridgeDJIFlightController.m

#import "RCTBridgeDJIFlightController.h"


#import "RCTBridgeDJIFlightController.h"

@interface RCTBridgeDJIFlightController ()
@property (nonatomic, nullable) DJIFlightControllerState *flightControllerState;
@end

@implementation RCTBridgeDJIFlightController

+ (instancetype)sharedFlightController {
    static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedFlightControllerInstance = [[self alloc] init];
    });
    return sharedFlightControllerInstance;
}

- (void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
    NSLog(@"State updated");
    self.flightControllerState = state;
}
@end

最终结果是您现在可以像这样使用它:

class VirtualStickController {
    func getFlightControllerState() {
        if let state = RCTBridgeDJIFlightController.shared.flightControllerState {
            print("FLIGHT CONTROLLER STATE: \(state)") // always null
        } else {
            print("NULL")
        }
    }
}

注意,因为sharedFlightController 现在是一个类属性,Swift/ObjC 互操作性足够智能,所以 Swift 代码可以只引用shared,如上所示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多