Appel de l'initialisateur Objective-C avec des arguments variés

J'essaie de réutiliser une class Objective-C, à savoir TSAlertView , dans un projet Swift. Le problème est que la class utilise un initialiseur avec des arguments variadiques. J'ai suivi la même approche suggérée à cette question stackoverflow et mon code fonctionne dans le simulateur iOS si j'utilise l'iPad Air mais PAS si j'utilise l'iPad Retina. Le code plante aussi sur un vrai iPad 3.

J'ai été capable de créer un exemple de jouet qui montre le même problème.

TestClass.h

#import <Foundation/Foundation.h> @interface TestClass : NSObject @property NSArray *titles; - (id)initWithTitle:(NSSsortingng *)title otherButtonTitlesVA:(va_list)args; @end 

TestClass.m

 #import "TestClass.h" @implementation TestClass - (id)initWithTitle:(NSSsortingng *)title otherButtonTitlesVA:(va_list)args { NSMutableArray *titles = [NSMutableArray array]; if ((self = [super init])) { [titles addObject:title]; id arg; if ((arg = va_arg(args, id)) && [arg isKindOfClass:[NSSsortingng class]]) { // this causes an EXC_BAD_ACCESS on iPad Retina [titles addObject:(NSSsortingng*)arg]; while ( nil != ( arg = va_arg( args, id ) ) ) { if ( ![arg isKindOfClass: [NSSsortingng class] ] ) return nil; [titles addObject:(NSSsortingng*)arg]; } } } self.titles = [NSArray arrayWithArray:titles]; return self; } @end 

TestClass + Swift.swift

 import Foundation extension TestClass { convenience init(title:Ssortingng?, otherButtonTitles:CVarArgType...) { self.init(title: title, otherButtonTitlesVA:getVaList(otherButtonTitles)) } } 

ViewController.swift

 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let testObject1 = TestClass(title: "First", otherButtonTitles: "Second", "Third") let testObject2 = TestClass(title: "First") // this causes the initializer to crash on iPad Retina NSLog("%@", testObject1.titles) NSLog("%@", testObject2.titles) } } 

Le code se bloque lorsque vous essayez de créer testObject2 avec un EXC_BAD_ACCESS . Y at-il quelque chose qui ne va pas avec mon code? Pourquoi l'iPad Air montre-t-il un comportement différent de l'iPad Retina?

EDIT: OK, je pense que le problème est que le code Objective-C attend une list terminée nil . Comment puis-je transmettre une list va_list terminée à va_list partir de Swift?

Comme vous l'avez déjà compris, le code Objective-C attend une list terminée nil . Sans cela, le comportement de

 if ((arg = va_arg(args, id)) && [arg isKindOfClass:[NSSsortingng class]]) { ... } 

est indéfini si la list des arguments réellement passés est épuisée.

nil est un pointeur NULL dans Objective-C, et vous pouvez l'append dans votre initialiseur de commodité:

 extension TestClass { convenience init(title:Ssortingng?, otherButtonTitles : CVarArgType...) { let nullPtr = UnsafePointer<Void>() let otherTitles : [CVarArgType] = otherButtonTitles + [nullPtr] self.init(title: title, otherButtonTitlesVA: getVaList(otherTitles)) } } 

Cela semble fonctionner correctement sur les plates-forms 32 et 64 bits.


Mise à jour pour Swift 3:

 extension TestClass { convenience init(title:Ssortingng?, otherButtonTitles : CVarArg...) { let otherTitles : [CVarArg] = otherButtonTitles + [Int(0)] self.init(title: title, otherButtonTitlesVA: getVaList(otherTitles)) } } 

Comme mentionné dans https://bugs.swift.org/browse/SR-5046 , l'utilisation d'un zéro de taille Int est la méthode recommandée pour transmettre un pointeur NULL sur une list d'arguments variable.