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?
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
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()
}
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.
[
UInt?
Int?
ouDouble?
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
}
}
}