67

我正在尝试Double?从 Objective-C 访问 Swift 类的属性。

class BusinessDetailViewController: UIViewController {

    var lat : Double?
    var lon : Double?

    // Other elements...
}

在另一个视图控制器中,我试图访问lat如下:

#import "i5km-Swift.h"
@interface ViewController ()

@property (strong, nonatomic) BusinessDetailViewController *businessDetailViewController;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.businessDetailViewController = [[BusinessDetailViewController alloc] initWithNibName:@"BusinessDetailViewController" bundle:nil];
    self.businessDetailViewController.lat = businessArray[1]; /* THIS GIVES ME AN ERROR */
}

我得到

在“BusinessDetailViewController *”类型的对象上找不到属性“lat”

为什么我无法访问此属性?我错过了什么?

4

4 回答 4

113

非 Objective-C 类型的可选值不会桥接到 Objective-C。也就是说,TestClass下面的前三个属性在 Objective-C 中可用,但第四个不会:

class TestClass: NSObject {
    var nsNumberVar: NSNumber = 0      // obj-c type, ok
    var nsNumberOpt: NSNumber?         // optional obj-c type, ok
    var doubleVar: Double = 0          // bridged Swift-native type, ok
    var doubleOpt: Double?             // not bridged, inaccessible
}

在您的 Objective-C 代码中,您可以像这样访问前三个属性:

TestClass *optTest = [[TestClass alloc] init];
optTest.nsNumberOpt = @1.0;
optTest.nsNumberVar = @2.0;
optTest.doubleVar = 3.0;

在您的情况下,您可以将lat和转换long为非可选或将它们切换为NSNumber.


lat请注意,如果您采用第二种方法(切换到lontype 的非可选属性),则需要小心您的 Objective-C 代码NSNumber——而 Swift 编译器会阻止您分配nil给非可选属性,Objective- C 编译器毫不犹豫地允许它,让nil值潜入您的 Swift 代码中,而在运行时没有机会捕获它们。考虑这种方法TestClass

extension TestClass {
    func badIdea() {
        // print the string value if it exists, or 'nil' otherwise
        println(nsNumberOpt?.stringValue ?? "nil")

        // non-optional: must have a value, right?
        println(nsNumberVar.stringValue)
    }
}

如果使用两个属性中的值调用,这可以正常工作,但如果从 Objective-C 代码nsNumberVar中设置为nil,这将在运行时崩溃。请注意,在使用之前无法检查是否nsNumberVar是!nil

TestClass *optTest = [[TestClass alloc] init];
optTest.nsNumberOpt = @1.0;
optTest.nsNumberVar = @2.0;
[optTest badIdea];
// prints 1, 2

optTest.nsNumberOpt = nil;
optTest.nsNumberVar = nil;
[optTest badIdea];
// prints nil, then crashes with an EXC_BAD_ACCESS exception
于 2014-10-14T17:04:06.367 回答
14

如果您的属性是 Swift 协议类型,只需@objc在其前面添加即可。

例子:

class Foo: UIViewController {
   var delegate: FooDelegate?
   ...
}

@objc protocol FooDelegate {
   func bar()
}
于 2015-11-05T03:39:25.420 回答
3

Optionals 是一个 swift 特定的功能,在 obj-c 中不可用。可选类实例之所以起作用,是因为 nil optional 可以映射到 nil 值,但值类型(int、floats 等)不是引用类型,因此这些类型的变量不存储引用,而是值本身。

我不知道是否有解决方案 - 一种可能的解决方法是创建非可选属性,将nil值映射到未使用的数据类型值(例如表示索引时为 -1,或表示坐标时为 999999):

class Test {
    var lat : Double? {
        didSet {
            self._lat = self.lat != nil ? self.lat! : 999999
        }
    }
    var lon : Double? {
        didSet {
            self._lon = self.lon != nil ? self.lon! : 999999
        }
    }

    var _lat: Double = 99999999
    var _lon: Double = 99999999
}

这应该将_latand_lon属性暴露给 obj-c。

请注意,我从未尝试过,所以请让我们知道它是否有效。

于 2014-10-14T17:07:16.297 回答
3

[UInt? Int?Double?属性] 不能标记为@objc,因为它的类型不能在 Objective-C 中表示。

但是,可以将它们“包装”在 NSNumber 中,如下所示:

class Foo {
    var bar: Double?
}

// MARK: Objective-C Support
extension Foo {
    /// bar is `Double?` in Swift and `(NSNumber * _Nullable)` in Objective-C
    @objc(bar)
    var z_objc_bar: NSNumber? {
        get {
            return bar as NSNumber?
        }
        set(value) {
            bar = value?.doubleValue ?? nil
        }
    }
}
于 2018-02-19T15:20:13.880 回答