无法从Objective-C访问Swift类型的属性

时间:2014-10-14 16:50:58

标签: objective-c swift types objective-c-swift-bridge

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

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 个答案:

答案 0 :(得分:105)

非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;

在您的情况下,您可以将latlong转换为非可选项,也可以将其切换为NSNumber的实例。


请注意,如果采用第二种方法(将latlon切换为NSNumber类型的非可选属性),则需要注意Objective-C代码 - 虽然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

答案 1 :(得分:12)

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

示例:

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

@objc protocol FooDelegate {
   func bar()
}

答案 2 :(得分:3)

Optionals是一个快速的特定功能,在obj-c中不可用。可选的类实例可以工作,因为可以将nil可选映射到nil值,但值类型(int,float等)是引用类型,因此这些类型的变量不会存储参考,但价值本身。

我不知道是否有解决方案 - 可能的解决方法是创建非可选属性,将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
}

这应该将_lat_lon属性公开给obj-c。

请注意,我从未尝试过,所以请告诉我们是否有效。

答案 3 :(得分:1)

  

[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
        }
    }
}