将单例从 Objective C 共享到 Swift
Posted
技术标签:
【中文标题】将单例从 Objective C 共享到 Swift【英文标题】:Share singleton from Objective C to 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 中分配它,那么你的代码中没有使用单例,而是一个普通的实例类型。 【参考方案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
语句并确认。
关于上面的第一个问题,我建议:
您不应该在 init
方法中使用这些注释行。如果您想确保使用您的单例对象,请将init
声明为NS_UNAVAILABLE
。
鉴于您的所有init
方法正在将flightControllerState
更新为nil
,您可以将其完全删除。在 ARC 中,属性会为您初始化为 nil
。
您不应在 @interface
中声明显式 ivar。让编译器自动为你合成。
你不应该@synthesize
@implementation
中的 ivar。编译器现在将自动为您合成(并将为 ivar 使用适当的名称,在属性名称中添加下划线。
您不应在 @implementation
中声明该全局变量。
如果你想在 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
,如上所示。
【讨论】:
以上是关于将单例从 Objective C 共享到 Swift的主要内容,如果未能解决你的问题,请参考以下文章
使用 Objective-C/Swift 单例模型,为啥我们要创建共享实例而不只是使用类方法? [复制]
如何使用 Swift 3 将单例与 Alamofire 一起使用?