Собственный базовый класс Swift или NSObject

105

Я протестировал некоторые isa swizzling с Swift и обнаружил, что он работает только тогда, когда NSObject является суперклассом (непосредственно или выше), или с использованием украшения '@objc'. В противном случае он будет следовать стилю статической и vtable-диспетчеризации, как C ++.

Нормально ли определять класс Swift без базового класса Cocoa / NSObject? Если это меня беспокоит, это означает отказ от большей части динамизма Objective-C, такой как перехват метода и самоанализ во время выполнения.

Динамическое поведение во время выполнения лежит в основе таких функций, как наблюдатели свойств, основные данные, аспектно-ориентированное программирование , обмен сообщениями высшего порядка , аналитические и журнальные структуры и т. Д.

Использование стиля вызова метода Objective-C добавляет к вызову метода около 20 операндов машинного кода, поэтому в определенных ситуациях ( много жестких вызовов методов с маленькими телами ) статическая отправка и отправка vtable в стиле C ++ могут работать лучше.

Но, учитывая общее правило 95-5 ( 95% прироста производительности достигается за счет настройки 5% кода ), не имеет ли смысла начинать с мощных динамических функций и укреплять там, где это необходимо?

Джаспер Блюз
источник
Связанный: Поддерживает ли Swift аспектно-ориентированное программирование? stackoverflow.com/a/24137487/404201
Джаспер Блюз

Ответы:

109

Классы Swift, являющиеся подклассами NSObject:

  • сами классы Objective-C
  • использовать objc_msgSend()для вызовов (большинства) их методов
  • предоставляют метаданные среды выполнения Objective-C для (большинства) реализаций своих методов

Классы Swift, не являющиеся подклассами NSObject:

  • являются классами Objective-C, но реализуют только несколько методов для совместимости с NSObject
  • не использовать objc_msgSend()для вызовов своих методов (по умолчанию)
  • не предоставляют метаданные времени выполнения Objective-C для своих реализаций методов (по умолчанию)

Создание подкласса NSObject в Swift дает вам гибкость выполнения Objective-C, но также и производительность Objective-C. Избегание NSObject может улучшить производительность, если вам не нужна гибкость Objective-C.

Редактировать:

В Xcode 6 beta 6 появляется динамический атрибут. Это позволяет нам указать Swift, что метод должен использовать динамическую отправку и, следовательно, будет поддерживать перехват.

public dynamic func foobar() -> AnyObject {
}
Грег Паркер
источник
1
Какую рассылку использует Swift вместо objc_msgSend? Это статично?
Билл
12
Swift может использовать отправку objc_msgSend, отправку виртуальных таблиц, прямую отправку или встраивание.
Грег Паркер
статический: менее 1,1 нс. vtable 1.1ns, msgSend 4.9ns. . (Конечно, зависит от оборудования). . Кажется, что «чистый» Swift - отличный язык для программирования на системном уровне, но для приложений я бы не хотел отказываться от динамических функций, хотя был бы рад, если бы они перешли на компилятор, как в «Сборке мусора» против ARC. . . Я слышал, что статическая диспетчеризация позволяет лучше прогнозировать ветвление (и, следовательно, производительность) в многоядерных системах. Правда?
Jasper Blues
«Классы Swift, которые не являются подклассами NSObject, являются классами Objective-C» - можете ли вы предоставить ссылку на то, где вы нашли это заявленное?
Мэтт С.
1
Таким образом, я должен создавать подклассы NSObject в Swift только в том случае, если мне нужно общаться с кодом Objective C?
MobileMon,
14

Я также обнаружил, что если базировать класс Swift на NSObject, я увидел некоторое неожиданное поведение во время выполнения, которое могло скрыть ошибки кодирования. Вот пример.

В этом примере, где мы не основываемся на NSObject, компилятор правильно определяет ошибку в testIncorrect_CompilerShouldSpot, сообщая «... 'MyClass' не может быть преобразован в 'MirrorDisposition'»

class MyClass {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

В этом примере, где мы основываемся на NSObject , компилятор не обнаруживает ошибку в testIncorrect_CompilerShouldSpot:

class myClass : NSObject {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

Думаю, мораль такова: только на основе NSObject, где вам действительно нужно!

Пит
источник
1
Спасибо за информацию.
Jasper Blues
13

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

Обратите внимание, что исключение суперкласса из объявления класса не назначает неявный базовый суперкласс любого типа. Он определяет базовый класс, который фактически станет корнем для независимой иерархии классов.

Из справочника по языку:

Классы Swift не наследуются от универсального базового класса. Классы, которые вы определяете без указания суперкласса, автоматически становятся базовыми классами, которые вы можете использовать.

Попытка сослаться superна класс без суперкласса (т.е. базового класса) приведет к ошибке времени компиляции.

'super' members cannot be referenced in a root class
Цезарь
источник
1
Да. Если вы попытаетесь создать исходный файл Cocoa или Cocoa Touch в контексте проекта, форма, в которую вы вставляете подкласс, фактически не позволит вам создать класс, оставив его пустым. Вы можете удалить суперкласс позже, когда он будет создан.
Cezar
1
Спасибо. Использование статической и vtable-диспетчеризации имеет смысл для системного программирования или настройки производительности. Для согласованности с родным Какао я думаю, что по умолчанию должен быть динамический . (Если кто-то не убедит меня в обратном, отсюда и этот вопрос).
Jasper Blues
1

Я считаю, что в подавляющем большинстве данных Swift не будет objc. Только те части, которые действительно должны взаимодействовать с инфраструктурой Objective C, будут явно отмечены как таковые.

Я не знаю, до какой степени самонаблюдение во время выполнения будет добавлено к языку. Перехват метода, вероятно, станет возможным только в том случае, если метод явно разрешает это. Это мое предположение, но только разработчики языков в Apple действительно знают, куда они на самом деле движутся.

Аналоговый файл
источник
Для меня имеет смысл сделать динамизм по умолчанию (путем расширения NSObject, использования украшения «@objc» или какого-либо другого подхода). Похоже, что при отсутствии базового класса Swift будет отдавать предпочтение отправке static / vtable, которая работает лучше, но в большинстве случаев в этом нет необходимости. . (90% прироста дает настройка 10% кода). Я надеюсь, что согласованность с динамизмом Objective-C - это отказ, а не согласие.
Jasper Blues
Поскольку язык разработан, это необязательно. Насколько нам известно, вполне может быть, что в будущем будут добавлены системные API-интерфейсы, которые изначально не являются objc. В среднесрочной / долгосрочной перспективе они могут даже перенести существующие библиотеки на код, отличный от objc, с тонким уровнем совместимости в objc, который вызывает код без objc. Мы просто не знаем. Вероятно, мы сможем заработать educated guessesчерез несколько лет. Но пока это просто большой вопросительный знак.
Аналоговый Файл
@JasperBlues Я бы сказал, что динамизм не нужен большую часть времени, и я бы предпочел иметь производительность по умолчанию, а затем выбрать динамическое поведение. Думаю, каждому свое :)
Лэнс
@ Лэнс Хммм. Может быть. Меня все еще беспокоят: наблюдатели, АОП, фреймворки тестовых имитаторов, аналитические фреймворки и т. Д. Что касается производительности, то 90% прироста достигается за счет настройки 10%. . так что это мое объяснение - подпишитесь на эти 10% случаев. Я думаю, что АОП будет иметь большое значение для корпоративных приложений iOS, но это можно сделать с помощью инструмента компиляции, как в C ++. . безусловно, разработчики игр, научных вычислений, графики и т. д. будут приветствовать более высокую производительность :)
Jasper Blues
1

Следующее скопировано из Swift-eBook от Apple и дает соответствующий ответ на ваш вопрос:

Определение базового класса

Любой класс, который не наследуется от другого класса, называется базовым классом.

Классы Swift не наследуются от универсального базового класса. Классы, которые вы определяете без указания суперкласса, автоматически становятся базовыми классами, которые вы можете использовать.


Ссылка

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251

Wottpal
источник
-6

Это нормально. Взгляните на цели дизайна Swift: цель состоит в том, чтобы избавиться от огромных классов проблем программирования. Смена методов, вероятно, не входит в число тех вещей, которые вы хотите делать со Swift.

скряга729
источник
3
Смена метода - это способ достичь шаблона перехвата во время выполнения. Одно из применений этого - применение сквозных задач в соответствии с принципами аспектно-ориентированного программирования. Собственные уведомления Apple, наблюдатели за собственностью (встроенные в Swift), основные данные - все это эффективно использует swizzling. . Одна из вещей, которая очаровала людей в Objective-C, несмотря на его неуклюжий синтаксис, - это динамизм, который позволяет легко предоставлять элегантные решения, когда требуется шаблон перехвата. . на самом деле для Objc не существовало формальной структуры АОП, потому что исходные материалы были очень хорошими.
Jasper Blues
Тот факт, что это делается сейчас, не означает, что это будет сделано завтра. Как вы говорите, уведомления, наблюдатели за свойствами, делегаты и т.п. предоставляются или могут быть предоставлены изначально. Язык будет развиваться, и мы не знаем, в каком направлении. Он получил много поддержки для функционального программирования. Но делает это необычным способом, поэтому я понятия не имею, сколько не хватает. Тем не менее, насколько нам известно, они могут двигаться к тому, чтобы препятствовать мутациям и поощрять чисто функциональное программирование. Что, если будущее UI будет реактивным? Пока нет возможности узнать.
Analog File
1
Да, но все это зависит от перехвата, который может выполняться двумя способами: во время компиляции (C ++) или во время выполнения (Ruby, Objective-C, Java (через asm, cglib, ApsectJ и т. Д.)). Современные языки обычно делают это во время выполнения. Людям нравился Objective-C, потому что он вел себя как современный язык, несмотря на то, что был старым бездельником. Как вы говорите, чем больше этого встроено в язык, тем лучше (при условии, что он сделан хорошо!). . но пока мы можем подключиться к наследию, предоставленному Objc / Foundation, поэтому я надеюсь, что расширение NSObject станет стандартной практикой для повседневных приложений в ближайшие несколько лет.
Jasper Blues