Взаимодействие с Objective C APIs
Функциональная совместимость является возможностью взаимодействовать через интерфейс между Swift и Objective C в любом направлении, позволяя Вам получить доступ и использовать части кода, записанного на одном языке в файле другого языка. Поскольку Вы начинаете интегрировать Swift в свой поток операций разработки приложений, это - хорошая идея понять, как можно эффективно использовать функциональную совместимость для переопределения, улучшить, и улучшить способ, которым Вы пишете приложения Какао.
Один важный аспект функциональной совместимости - то, что она позволяет Вам работать с Objective C APIs при записи Кода SWIFT. После импорта платформы Objective C можно инстанцировать классов от нее и взаимодействовать с ними использующий собственный синтаксис Swift.
Инициализация
Для инстанцирования класса Objective C в Swift Вы вызываете один из его инициализаторов с синтаксисом Swift. Когда Objective C init
методы приезжают в Swift, они берут собственный синтаксис инициализатора Swift. Префикс «init» отрезан и становится ключевым словом, чтобы указать, что метод является инициализатором. Для init
также, отрезаны методы, начинающиеся с «initWith», «С». Первая буква селекторной части, имевшей «init» или «initWith», откололась от него, становится нижним регистром, и что селекторная часть обрабатывается как имя первого параметра. Остальная часть селекторных частей также соответствует именам параметра. Каждая селекторная часть идет в круглых скобках и требуется на сайте вызова.
Например, где в Objective C Вы сделали бы это:
Objective C
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
В Swift Вы делаете это:
Swift
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
Вы не должны вызывать alloc
; Swift правильно обрабатывает это для Вас. Заметьте, что «init» не появляется нигде при вызове инициализатора стиля Swift.
Можно быть явными во вводе объекта во время инициализации, или можно опустить тип. Вывод типа Swift правильно определяет тип объекта.
Swift
let myTextField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 40.0))
Они UITableView
и UITextField
объекты имеют ту же знакомую функциональность, которую они имеют в Objective C. Можно использовать их таким же образом, Вы были бы в Objective C, получая доступ к любым свойствам и вызывая любые методы, определенные на соответствующих классах.
Для непротиворечивости и простоты, методы фабрики Objective C отображаются как инициализаторы удобства в Swift. Это отображение позволяет им использоваться с тем же кратким, ясным синтаксисом в качестве инициализаторов. Например, тогда как в Objective C Вы вызвали бы этот метод фабрики как это:
Objective C
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
В Swift Вы вызываете его как это:
Swift
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
Инициализация Failable
В Objective C инициализаторы непосредственно возвращают объект, который они инициализируют. Для информирования вызывающей стороны, когда инициализация перестала работать инициализатор Objective C может возвратиться nil
. В Swift этот образец встроен в функцию языка, названную ❮failable инициализацией ❯. Много инициализаторов Objective C в iOS и системных платформах OS X контролировались, чтобы указать, может ли инициализация перестать работать. Эти инициализаторы Objective C импортируются как также init(...)
— если инициализация не может перестать работать – или init?(...)
если инициализация может перестать работать. В Ваших собственных классах Objective C и классах в платформах, еще не контролировавшихся, инициализаторы импортируются как init!(...)
. Например, UIImage(contentsOfFile:)
инициализатор может не инициализировать a UIImage
возразите, не существует ли файл образа в предоставленном пути. Если инициализация успешна, можно использовать дополнительную привязку для разворачивания результата failable инициализатора.
Swift
if let image = UIImage(contentsOfFile: "MyImage.png") {
// loaded the image successfully
} else {
// could not load the image
}
Доступ к свойствам
Свойства доступа и набора на Objective C возражают в Swift с помощью точечного синтаксиса.
Swift
myTextField.textColor = UIColor.darkGrayColor()
myTextField.text = "Hello world"
При получении или установке свойства, используйте имя свойства, не добавляя круглые скобки. Заметьте это darkGrayColor
имеет ряд круглых скобок. Это вызвано тем, что darkGrayColor
метод класса для UIColor
, не свойство.
В Objective C метод, возвращающий значение и не берущий параметров, может быть обработан как неявный метод get — и вызван использование того же синтаксиса как метод get для свойства. Дело обстоит не так в Swift. В Swift, только свойства, записанные с помощью @property
синтаксис в Objective C импортируется как свойства. Методы импортируют и вызывают, как описано в Работе с Методами.
Работа с методами
При вызове методов Objective C от Swift используйте точечный синтаксис.
Когда методы Objective C приезжают в Swift, первая часть селектора Objective C становится именем базового метода и появляется вне круглых скобок. Первый параметр сразу появляется в круглых скобках без имени. Остальная часть селекторных частей соответствует именам параметра и идет в круглых скобках. Все селекторные части требуются на сайте вызова.
Например, тогда как в Objective C Вы сделали бы это:
Objective C
[myTableView insertSubview:mySubview atIndex:2];
В Swift Вы делаете это:
Swift
myTableView.insertSubview(mySubview, atIndex: 2)
При вызове метода без параметров необходимо все еще включать круглые скобки.
Swift
myTableView.layoutIfNeeded()
Совместимость ID
Swift включает названный тип протокола AnyObject
это представляет любой вид объекта, так же, как id
делает в Objective C. AnyObject
протокол позволяет Вам писать безопасный с точки зрения типов Код SWIFT при поддержании гибкости невведенного объекта. Из-за дополнительной безопасности, предоставленной AnyObject
протокол, импорт Swift id
как AnyObject
.
Например, как с id
, можно присвоить объект любого типа класса к константе или переменной, введенной как AnyObject
. Можно также повторно присвоить переменную объекту другого типа.
Swift
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()
Можно также вызвать любой метод Objective C и доступ любое свойство, не бросая к более определенному типу класса. Это включает Objective C совместимые методы, отмеченные с @objc
атрибут.
Swift
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Однако, потому что определенный тип объекта, введенного как AnyObject
не известен до времени выполнения возможно непреднамеренно записать небезопасный код. Как в Objective C, если Вы вызываете метод или получаете доступ к свойству, не существующему на AnyObject
типизированный объект, это - ошибка периода выполнения. Например, следующие компиляции кода без жалобы и затем вызывают нераспознанную селекторную ошибку во время выполнения:
Swift
myObject.characterAtIndex(5)
// crash, myObject doesn't respond to that method
Можно использовать в своих интересах optionals в Swift для устранения этой общей ошибки Objective C из кода. Когда Вы обращаетесь к методу Objective C AnyObject
текстовый объект, вызов метода фактически ведет себя как неявно развернутый дополнительный. Можно использовать тот же дополнительный синтаксис объединения в цепочку, который Вы использовали бы для дополнительных методов в протоколах для дополнительного вызова метода на AnyObject
.
Например, в листинге кода ниже, первые и вторые строки не выполняются потому что count
свойство и characterAtIndex:
метод не существует на NSDate
объект. myCount
постоянный выведен, чтобы быть дополнительным Int
, и установлен в nil
. Можно также использовать if
–let
оператор для условного разворачивания результата метода, что объект может не ответить на, как показано на строке три.
Swift
let myCount = myObject.count
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex?(5) {
println("Found \(fifthCharacter) at index 5")
}
Как со всем downcasts в Swift, бросающем от AnyObject
к более конкретному объекту тип, как гарантируют, не успешно выполнится и поэтому возвращает дополнительное значение. Можно проверить, что дополнительное значение, чтобы определить, успешно выполнился ли бросок.
Swift
let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
println("\(date.timeIntervalSinceReferenceDate)")
}
Конечно, если Вы уверены в типе объекта (и знайте, что это не nil
), можно вызвать вызов с as
оператор.
Swift
let myDate = lastRefreshDate as NSDate
let timeInterval = myDate.timeIntervalSinceReferenceDate
Работа с нолем
В Objective C Вы работаете со ссылками на объекты с помощью необработанных указателей, которые могли быть NULL
(также называемый как nil
в Objective C). В Swift все значения — включая структуры и ссылки на объект — как гарантируют, будут ненолем. Вместо этого Вы представляете значение, которое могло отсутствовать путем обертывания типа значения в дополнительном типе. Когда необходимо указать, что значение отсутствует, Вы используете значение nil
. Для получения дополнительной информации о optionals, посмотрите Optionals в Swift Язык программирования.
Поскольку Objective C не делает гарантий, что объект является ненолем, Swift делает все классы в типах аргумента и типах возврата дополнительными в импортированном Objective C APIs. Перед использованием объекта Objective C необходимо проверить, чтобы гарантировать, что он не отсутствует.
В некоторых случаях Вы могли бы быть абсолютно уверены, что метод Objective C или свойство никогда не возвращают a nil
ссылка на объект. Для создания объектов в этом специальном сценарии более удобными для работы с Swift импортирует типы объектов, как неявно развернуто optionals. Неявно развернутые дополнительные типы включают все функции безопасности дополнительных типов. Кроме того, можно получить доступ к значению непосредственно, не проверяя на nil
или разворачивание его самостоятельно. При доступе к значению в этом виде дополнительного типа, безопасно не разворачивая его сначала, неявно развернутые дополнительные проверки, отсутствует ли значение. Если значение отсутствует, ошибка периода выполнения происходит. В результате необходимо всегда проверять и разворачивать неявно развернутый дополнительный сами, если Вы не уверены, что не может отсутствовать значение.
Расширения
Расширение Swift подобно категории Objective C. Расширения разворачивают поведение существующих классов, структур и перечислений, включая определенных в Objective C. Можно определить расширение на типе или от системной платформы или от одного из собственных типов. Просто импортируйте надлежащий модуль и обратитесь к классу, структуре или перечислению тем же именем, которое Вы использовали бы в Objective C.
Например, можно расшириться UIBezierPath
класс для создания простого пути Bézier с равносторонним треугольником, на основе предоставленной длины стороны и начальной точки.
Swift
extension UIBezierPath {
convenience init(triangleSideLength: CGFloat, origin: CGPoint) {
self.init()
let squareRoot = CGFloat(sqrt(3.0))
let altitude = (squareRoot * triangleSideLength) / 2
moveToPoint(origin)
addLineToPoint(CGPoint(x: origin.x + triangleSideLength, y: origin.y))
addLineToPoint(CGPoint(x: origin.x + triangleSideLength / 2, y: origin.y + altitude))
closePath()
}
}
Можно использовать расширения для добавления свойств (включая класс и статические свойства). Однако эти свойства должны быть вычислены; расширения не могут добавить сохраненные свойства к классам, структурам или перечислениям.
Этот пример расширяется CGRect
структура для содержания вычисленного area
свойство:
Swift
extension CGRect {
var area: CGFloat {
return width * height
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
let area = rect.area
// area: CGFloat = 500.0
Можно также использовать расширения для добавления соответствия протокола к классу, не разделяя его на подклассы. Если протокол определяется в Swift, можно также добавить соответствие к нему к структурам или перечислениям, определенный ли в Swift или Objective C.
Вы не можете использовать расширения для переопределения существующих методов или свойств на типах Objective C.
Закрытия
Блоки Objective C автоматически импортируются как закрытия Swift. Например, вот основная переменная Objective C:
Objective C
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {/* ... */}
И вот то, на что это похоже в Swift:
Swift
let completionBlock: (NSData, NSError) -> Void = {data, error in /* ... */}
Закрытия Swift и блоки Objective C совместимы, таким образом, можно передать закрытия Swift методам Objective C, ожидающим блоки. Закрытия Swift и функции имеют тот же тип, таким образом, можно даже передать имя функции Swift.
Закрытия имеют подобную семантику получения как блоки, но отличаются одним ключевым способом: Переменные являются непостоянными, а не скопированы. Другими словами, поведение __block
в Objective C поведение по умолчанию для переменных в Swift.
Объектное сравнение
Существует два отличных типа сравнения при сравнении двух объектов в Swift. Первое, равенство (==
), сравнивает содержание объектов. Второе, идентификационные данные (===
), определяет, относятся ли константы или переменные к тому же экземпляру объекта.
Swift и объекты Objective C обычно сравниваются в Swift с помощью ==
и ===
операторы. Swift обеспечивает реализацию по умолчанию ==
оператор для объектов, происходящих из NSObject
класс. В реализации этого оператора Swift вызывает isEqual:
метод, определенный на NSObject
класс. NSObject
класс только выполняет сравнение идентификационных данных, таким образом, необходимо реализовать собственное isEqual:
метод в классах, происходящих из NSObject
класс. Поскольку можно передать объекты Swift (включая, не полученные из NSObject
) к Objective C APIs необходимо реализовать isEqual:
метод для этих классов, если Вы хотите, чтобы Objective C APIs сравнил содержание объектов, а не их идентификационных данных.
Как часть реализации равенства для Вашего класса, убедиться реализовать hash
свойство согласно правилам в Объектном сравнении. Далее, если Вы хотите использовать свой класс в качестве ключей в словаре, также соответствовать Hashable
протокол и реализация hashValue
свойство.
Соответствие типов Swift
Когда Вы создаете класс Swift, убывающий от класса Objective C, класса и его элементов — свойства, методы, нижние индексы, и инициализаторы — автоматически доступны от Objective C. В некоторых случаях Вам нужно более прекрасное гранулярное управление тем, как Ваш Swift API представлен Objective C. Можно использовать @objc
припишите, если Ваш класс Swift не наследовался от класса Objective C, или если Вы хотите изменить имя символа в Вашем интерфейсе, как это представлено коду Objective C. Можно также использовать dynamic
модификатор, чтобы потребовать, что доступ к элементам, которые будут динамично диспетчеризированы в течение времени выполнения Objective C, если Вы используете APIs как значение ключа, замечая, которые динамично заменяют реализацию метода.
Представление Swift интерфейсы в Objective C
Когда Вы определяете класс Swift, наследовавшийся от NSObject
или любой другой класс Objective C, класс автоматически совместим с Objective C. Все шаги в этом разделе были уже выполнены для Вас компилятором Swift. Если Вы никогда не импортируете класс Swift в коде Objective C, Вы не должны волноваться о соответствии типов в этом случае также. Иначе, если Ваш класс Swift не происходит из класса Objective C, и Вы хотите использовать его от кода Objective C, можно использовать @objc
атрибут, описанный ниже.
@objc
атрибут делает Ваш Swift API доступным в Objective C и время выполнения Objective C. Другими словами, можно использовать @objc
атрибут перед любым методом Swift, свойством, нижним индексом, инициализатором или классом, который Вы хотите использовать от кода Objective C. Если Ваш класс наследовался от класса Objective C, компилятор вставляет атрибут для Вас. Компилятор также добавляет атрибут к каждому элементу в классе, самостоятельно отмеченном с @objc
атрибут. Когда Вы используете @IBOutlet
, @IBAction
, или @NSManaged
атрибут, @objc
атрибут добавляется также. Этот атрибут также полезен, когда Вы работаете с классами Objective C, использующими селекторы для реализации шаблона разработки целевого действия — например, NSTimer
или UIButton
.
При использовании Swift API от Objective C компилятор обычно выполняет прямой перевод. Например, Swift API func playSong(name: String)
импортируется как - (void)playSong:(NSString *)name
в Objective C. Однако существует одно исключение: при использовании инициализатора Swift в Objective C компилятор добавляет текст «initWith» к началу метода и должным образом капитализирует первый символ в исходном инициализаторе. Например, этот инициализатор Swift init (songName: String, artist: String)
импортируется как - (instancetype)initWithSongName:(NSString *)songName artist:(NSString *)artist
в Objective C.
Swift также обеспечивает вариант @objc
атрибут, позволяющий Вам указывать имя для своего символа в Objective C. Например, если имя Вашего класса Swift содержит символ, не поддерживающийся Objective C, можно обеспечить альтернативное имя для использования в Objective C. При обеспечении имени Objective C для функции Swift используйте синтаксис селектора Objective C. Не забудьте добавлять двоеточие (:
) везде, где параметр следует за селекторной частью.
Swift
@objc(Squirrel)
class Белка {
@objc(initWithName:)
init (имя: String) { /*...*/ }
@objc(hideNuts:inTree:)
func прячьОрехи(Int, вДереве: Дерево) { /*...*/ }
}
Когда Вы используете @objc(
имя)
атрибут на классе Swift, класс сделан доступным в Objective C без любого пространства имен. В результате этот атрибут может также быть полезным при миграции archivable класса Objective C Swift. Поскольку архивные объекты хранят имя своего класса в архиве, необходимо использовать @objc(
имя)
атрибут для указания того же имени как класс Objective C так, чтобы более старые архивы могли быть разархивированы новым классом Swift.
Требование динамической отгрузки
В то время как @objc
атрибут представляет Ваш Swift API времени выполнения Objective C, это не гарантирует динамическую отгрузку свойства, метода, нижнего индекса или инициализатора. Компилятор Swift может все еще devirtualize или встраивать задействованный доступ для оптимизации производительности кода, обходя время выполнения Objective C. Когда Вы отмечаете объявление элемента с dynamic
модификатор, доступ к тому элементу всегда динамично диспетчеризируется. Поскольку объявления, отмеченные с dynamic
модификатор диспетчеризируется с помощью времени выполнения Objective C, они неявно отмечены с @objc
атрибут.
Требование динамической отгрузки редко необходимо. Однако необходимо использовать dynamic
модификатор, когда Вы знаете, что реализация API заменяется во время выполнения. Например, можно использовать method_exchangeImplementations
функция во время выполнения Objective C для выгрузки реализации метода, в то время как работает приложение. Если бы компилятор Swift встроил реализацию метода или devirtualized доступа к нему, то новая реализация не использовалась бы.
Селекторы Objective C
Селектор Objective C является типом, относящимся к имени метода Objective C. В Swift селекторы Objective C представлены Selector
структура. Можно создать селектор со строковым литералом, такой как let mySelector: Selector = "tappedButton:"
. Поскольку строковые литералы могут быть автоматически преобразованы в селекторы, можно передать строковый литерал любому методу, принимающему селектор.
Swift
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
myButton.addTarget(self, action: "tappedButton:", forControlEvents: .TouchUpInside)
}
func tappedButton(sender: UIButton!) {
println("tapped button")
}
required init(coder: NSCoder) {
super.init(coder: coder)
}
}
Если Ваш класс Swift наследовался от класса Objective C, все методы и свойства в классе доступны как селекторы Objective C. Иначе, если Ваш класс Swift не наследовался от класса Objective C, необходимо снабдить префиксом символ, который Вы хотите использовать в качестве селектора с @objc
атрибут, как описано в Swift Соответствие типов.