【问题标题】:Cannot access property on Swift type from Objective-C无法从 Objective-C 访问 Swift 类型的属性
【发布时间】:2014-12-09 13:23:46
【问题描述】:

我正在尝试从 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”

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

【问题讨论】:

  • 我不知道,因为在businessDetailViewController中,我也有一个插座@IBOutlet var mapView: MKMapView?,我可以从外部访问它,但它没有public
  • 我认为可选结构在 Objective-C 中是不可见的。将验证。

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


【解决方案1】:

非 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

【讨论】:

  • 我无法在文档中找到它,但编译器会告诉你它不能用 @objc protocol FooProtocol { var lat:Double? {get} } 表示
  • 这方面的文档不完整——例如,this page 说在 Swift 中定义的枚举和结构在 Obj-C 中不可用,但没有提到 Optionals(实现为enum) 和大多数内置的 struct 类型的桥接正确,直到您到达 the page on Cocoa data types
  • @NateCook 我无法证明这一点,但我认为可选的NSNumber 是桥接的,可选的IntFloat 不是因为NSNumber 是引用类型,而@ 987654348@ 映射到 nil,而值类型在 objc 中没有 nil 对应项。我不是在纠正你,只是想知道你是否同意
  • @Antonio:没错——在 Objective-C 中,任何引用类型都可以是 nil,因此它们会自动桥接到 Swift 选项。事实上,有人可能会争辩说,在桥接项目中,任何引用类型都不应该是非可选的,因为您甚至可以将 nil 分配给来自 Objective-C 的非可选属性,从而导致潜在的崩溃而没有追索权。看看我的补充......
  • 很高兴知道 - 不要在 objc 端使用桥接,但我会记住这一点(这只是时间问题 :))
【解决方案2】:

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

例子:

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

@objc protocol FooDelegate {
   func bar()
}

【讨论】:

    【解决方案3】:

    Optionals 是 swift 特有的特性,在 obj-c 中不可用。可选类实例之所以有效,是因为 nil 可选可以映射到 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
    }
    

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

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

    【讨论】:

    • 试一试,是的,我们可以从外部访问 _lat 和 _lon。谢谢。
    【解决方案4】:

    [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-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-28
      相关资源
      最近更新 更多