web-dev-qa-db-fra.com

Impossible d’accéder à la propriété sur Swift à partir d’Objective-C

J'essaie d'accéder à la propriété Double? De la classe Swift) à partir d'Objective-C.

class BusinessDetailViewController: UIViewController {

    var lat : Double?
    var lon : Double?

    // Other elements...
}

Dans un autre contrôleur de vue, j'essaie d'accéder à lat comme suit:

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

et j'obtiens

La propriété 'lat' est introuvable sur l'objet de type 'BusinessDetailViewController *'

Pourquoi ne puis-je pas accéder à cette propriété? Qu'est-ce que je rate?

60
tranvutuan

Les valeurs facultatives des types non-Objective-C ne sont pas pontées dans Objective-C. C'est-à-dire que les trois premières propriétés de TestClass ci-dessous seraient disponibles dans Objective-C, mais pas la quatrième:

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
}

Dans votre code Objective-C, vous accéderiez à ces trois premières propriétés comme ceci:

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

Dans votre cas, vous pouvez convertir lat et long en non-facultatif ou les remplacer par des instances de NSNumber.


Notez que vous devez faire attention à votre code Objective-C si vous prenez la deuxième approche (passer de lat et lon à des propriétés non optionnelles de type NSNumber) - alors que le compilateur Swift vous empêchera d'affecter nil à des propriétés non facultatives, le compilateur Objective-C n'aura aucun scrupule à l'autoriser, laissant nil valeurs faufiler dans votre code Swift sans aucune chance de les attraper au moment de l'exécution. Considérez cette méthode sur 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)
    }
}

Cela fonctionne correctement s'il est appelé avec des valeurs dans les deux propriétés, mais si nsNumberVar est défini sur nil à partir du code Objective-C, le système plantera à l'exécution. Notez qu'il n'y a aucun moyen de vérifier si nsNumberVar est ou non nil avant de l'utiliser!

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
105
Nate Cook

Si votre propriété est un type de protocole Swift, ajoutez simplement @objc devant lui.

Exemple:

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

@objc protocol FooDelegate {
   func bar()
}
14
dmzza

Optionals est une fonctionnalité spécifique à Swift, non disponible dans obj-c. Les instances de classe facultatives fonctionnent, car une option nil peut être mappée sur une valeur nil, mais des types de valeur (int, floats, etc.) are not types de référence, donc les variables de ces types ne stockent pas une référence, mais la valeur elle-même.

Je ne sais pas s'il existe une solution - une solution de contournement possible consiste à créer des propriétés non facultatives mappant la valeur nil sur une valeur de type de données inutilisée (telle que -1 lorsque vous représentez un index ou 999999 pour une coordonnée):

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
}

Cela devrait exposer le _lat et _lon propriétés à obj-c.

Notez que je n’ai jamais essayé, merci de nous faire savoir si cela fonctionne.

3
Antonio

[UInt?Int? ou Double? propriétés] ne peut pas être marqué @objc car son type ne peut pas être représenté dans Objective-C.

Il est cependant possible de les "envelopper" dans un NSNumber comme ceci:

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
        }
    }
}
1
Axel Guilmin