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

Cannot access Objective-C singleton's array from Swift code

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

// 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

我可以从我的 objective C 代码访问名为 imgArray 的数组。但是,在 swift 中,当我这样做时出现错误:

let array  = MyManager.sharedManager()

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

我可以访问 MyManager.sharedManager(),但为什么我不能像 objective C 一样访问 imgArray

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

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

+ (MyManager *)sharedManager;

+ (instancetype)sharedManager;

一些建议:

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

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

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

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

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

  2. 我建议您审核 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 代码中减少不必要的可选项解包。

  3. 顺便说一句,请注意我没有声明实例变量。 (如果你确实需要一个,我不建议在 public 接口中声明它。)Objective-C 现在会自动为我们合成支持我们属性的 ivars。 (所以我的 属性 images 将有一个名为 _images 的 ivar 将为我合成。)而且你也不会 need/want @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