无法从 Swift 代码访问 Objective-C 单例的数组

Posted

技术标签:

【中文标题】无法从 Swift 代码访问 Objective-C 单例的数组【英文标题】:Cannot access Objective-C singleton's array from Swift code 【发布时间】:2019-03-04 23:28:34 【问题描述】:

我在单例中创建了一个数组,以便从我的代码的多个部分将对象写入其中。方法如下:

// in singleton.h
#import <UIKit/UIKit.h>

// make globally accessible array
@interface MyManager : NSObject 
    NSMutableArray *imgArray;

@property (nonatomic, retain) NSMutableArray *imgArray;
+ (id)sharedManager;
@end

// in singleton.m
#import "singleton.h"

对于我的 .m 文件:

@implementation MyManager

@synthesize imgArray;

#pragma mark Singleton Methods

+ (id)sharedManager 
    static MyManager *sharedMyManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        sharedMyManager = [[self alloc] init];
    );
    return sharedMyManager;


- (id) init 
    if (self = [super init]) 
        self.imgArray = [NSMutableArray new];
        
    NSLog(@"initialized");
    return self;


@end

我可以从我的目标 C 代码中访问名为 imgArray 的数组。但是,当我这样做时,我很快就会得到一个错误:

let array  = MyManager.sharedManager()

array.imgArray.add("hello world") .    (!!!) Value of type 'Any?' has no member 'imgArray'

我可以访问MyManager.sharedManager(),但是为什么我不能像在目标C中那样访问imgArray

【问题讨论】:

【参考方案1】:

您应该将其声明为instancetypeMyManager *。例如

+ (MyManager *)sharedManager;

+ (instancetype)sharedManager;

几个建议:

    单例的 Swift 约定是使用 shared 的类属性名称,而不是 sharedManager() 的类方法。当你在 Objective-C 中声明它时,你可能想要明确地说它是一个类属性:

    @property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
    

    这不会改变任何 Objective-C 的行为,但在 Swift 中,你可以这样做:

    let manager = MyManager.shared
    manager.images.add(image)
    

    这会产生更简洁和惯用的 Swift 代码。

    我建议您审核 Objective-C 的可空性。即,确认什么可以是nil,什么不能。因为imgArray(我可能只是称之为images)和sharedManager都不能是nil,所以我只会使用NS_ASSUME_NONNULL_BEGIN/END宏告诉编译器“除非我另有说明,假设这个属性不能是nil”:

    //  MyManager.h
    
    @import UIKit;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface MyManager : NSObject
    
    @property (nonatomic, strong) NSMutableArray <UIImage *> *images;
    @property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    通过告诉编译器这两个不能是 nil,这意味着您将不得不在 Swift 代码中减少不必要的可选项解包。

    顺便说一句,请注意我没有声明实例变量。 (如果你确实需要,我不建议在公共接口中声明它。)Objective-C 现在将自动为我们合成支持我们属性的 ivars。 (所以我的属性 images 将有一个名为 _images 的 ivar,它将为我合成。)而且您也不需要/想要 @synthesize 行:

    //  MyManager.m
    
    #import "MyManager.h"
    
    @implementation MyManager
    
    + (instancetype)sharedManager 
        static MyManager *sharedMyManager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^
            sharedMyManager = [[self alloc] init];
        );
        return sharedMyManager;
    
    
    - (instancetype)init 
        if (self = [super init]) 
            self.images = [NSMutableArray new];
        
        NSLog(@"initialized");
        return self;
    
    
    @end
    

【讨论】:

【参考方案2】:

+ (id)sharedManager; 更改为+ (MyManager *)sharedManager;。否则 Swift 不知道 sharedManager 是什么类型的对象,它会假定它是 Any

【讨论】:

或者instancetype,而不是id

以上是关于无法从 Swift 代码访问 Objective-C 单例的数组的主要内容,如果未能解决你的问题,请参考以下文章

无法从 Objective-C 视图控制器访问 Swift var - iOS

项目在 Swift 中时无法从 Objective-C 访问 swift 类

Objective C to Swift代码转换,似乎无法在Swift中正确处理字节

从 Swift 访问 Objective-C 变量/函数

Swift 类的 Objective-c 协议。无法识别的方法

如何从 Swift 单元测试访问测试目标中存在的 Objective-C 代码?