Не удается получить доступ к свойству типа Swift из Objective-C

Я пытаюсь получить доступ к классу Swift Double? свойство из Objective-C.

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

и я получаю

Свойство 'lat' не найдено для объекта типа 'BusinessDetailViewController *'

Почему я не могу получить доступ к этой собственности? Что мне не хватает?

4 ответа

Решение

Необязательные значения типов не-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,


Обратите внимание, что вы должны быть осторожны с вашим кодом Objective C, если вы выбираете второй подход (переключение lat а также lon на необязательные свойства типа 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)
    }
}

Это прекрасно работает, если вызывается со значениями в обоих свойствах, но если nsNumberVar установлен в nil из кода Objective-C, это будет сбой во время выполнения. Обратите внимание, что нет способа проверить, действительно ли 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

Если ваше свойство является типом протокола Swift, просто добавьте @objc перед ней.

Пример:

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

@objc protocol FooDelegate {
   func bar()
}

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

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.

Обратите внимание, что я никогда не пробовал, поэтому, пожалуйста, дайте нам знать, если это работает.

Другие вопросы по тегам