Как понять различие между class_eval () и instance_eval ()?

Вы также можете получить эту проблему, если у вас есть сценарий кода вашего контейнера приложения Swift 2.3, и вы создаете новое расширение в Xcode 8.

Чтобы решить вышеупомянутый сценарий ...

Примечание: Весь код должен быть в одной и той же быстрой (совместимой) версии для компиляции без сбоев.

При этом, одним из способов решения этой проблемы является , придерживаясь Swift 2.3 , а затем устанавливая для вашей цели расширения Использовать Legacy Swift Language Version значение «Да» .

Вы можете найти эту опцию, когда Xcode 8 открыт, следующим образом:

  1. Выберите корень проекта вашего приложения в Навигаторе проектов (слева)
  2. На вкладке с правой стороны выберите расширение в разделе TARGETS
  3. . После выбора расширения нажмите вкладку Настройки сборки
  4. Прокрутите вниз и найдите Использовать Legacy Swift Language Version и установите его в Да из его раскрывающегося меню.
  5. Теперь вы можете построить проект

Примечание: Возможно, вам придется исправить переопределенный код в шаблонах расширений, поскольку они изначально были в Swift 3.

54
задан pez_dispenser 22 May 2009 в 12:32
поделиться

4 ответа

Как сказано в документации, class_eval оценивает строку или блок в контексте модуля или класса. Таким образом, следующие фрагменты кода эквивалентны:

class String
  def lowercase
    self.downcase
  end
end

String.class_eval do
  def lowercase
    self.downcase
  end
end

В каждом случае класс String был повторно открыт и определен новый метод. Этот метод доступен для всех экземпляров класса, поэтому:

"This Is Confusing".lowercase 
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"

class_eval имеет ряд преимуществ перед простым повторным открытием класса. Во-первых, вы можете легко вызвать его для переменной, и вам будет ясно, каковы ваши намерения. Еще одно преимущество состоит в том, что он не сработает, если класс не существует. Таким образом, приведенный ниже пример завершится ошибкой, поскольку Массив написан неправильно. Если бы класс был просто повторно открыт, он был бы успешным (и был бы определен новый неправильный класс Aray ):

Aray.class_eval do
  include MyAmazingArrayExtensions
end

Наконец, class_eval может принимать строку, что может быть полезно, если вы делаете что-то более гнусное ...

instance_eval , с другой стороны, оценивает код для одного экземпляра объекта:

confusing = "This Is Confusing"
confusing.instance_eval do
  def lowercase
    self.downcase
  end
end   

confusing.lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String

Итак, с instance_eval метод определен только для этого единственного экземпляра строки.

Итак, почему instance_eval в Class определяет методы класса?

Так же, как «Это сбивает с толку» и «Смиты в автобусе Чарли» являются экземплярами String , Array , String , Hash и все другие классы сами являются экземплярами Class . Вы можете проверить это, позвонив им по номеру #class :

"This Is Confusing".class
=> String

String.class
=> Class

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

90
ответ дан 7 November 2019 в 07:53
поделиться

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

Вот ссылка на одноэлементные методы и одноэлементный шаблон (не специфичный для Ruby )

3
ответ дан 7 November 2019 в 07:53
поделиться

Я думаю, вы ошиблись. class_eval добавляет метод в класс, поэтому все экземпляры будут иметь метод. instance_eval добавит метод только к одному конкретному объекту.

foo = Foo.new
foo.instance_eval do
  def instance_bar
    "instance_bar"
  end
end

foo.instance_bar      #=> "instance_bar"
baz = Foo.new
baz.instance_bar      #=> undefined method
5
ответ дан 7 November 2019 в 07:53
поделиться

Другой ответ правильный, но позвольте мне немного углубиться.

Ruby имеет несколько различных видов области видимости; шесть согласно википедии , хотя подробная формальная документация, похоже, отсутствует. В этом вопросе используются следующие области: неудивительно, что экземпляр и класс .

Текущая область действия экземпляра определяется значением self . Все неквалифицированные вызовы методов отправляются текущему экземпляру, как и любые ссылки на переменные экземпляра (которые выглядят как @this ).

Однако def не является вызовом метода. Целью для методов, созданных def , является текущий класс (или модуль), который можно найти с помощью Module.nesting [0] .

Давайте посмотрим, как два разных eval разновидности влияют на эти области:

String.class_eval {[self, Module.nesting [0]]} Все неквалифицированные вызовы методов отправляются текущему экземпляру, как и любые ссылки на переменные экземпляра (которые выглядят как @this ).

Однако def не является вызовом метода. Целью для методов, созданных def , является текущий класс (или модуль), который можно найти с помощью Module.nesting [0] .

Давайте посмотрим, как два разных eval разновидности влияют на эти области:

String.class_eval {[self, Module.nesting [0]]} Все неквалифицированные вызовы методов отправляются текущему экземпляру, как и любые ссылки на переменные экземпляра (которые выглядят как @this ).

Однако def не является вызовом метода. Целью для методов, созданных def , является текущий класс (или модуль), который можно найти с помощью Module.nesting [0] .

Давайте посмотрим, как два разных eval разновидности влияют на эти области:

String.class_eval {[self, Module.nesting [0]]} => [Строка, Строка] String.instance_eval {[сам, Module.nesting [0]]} => [Строка, # <Класс: Строка>]

В обоих случаях область действия экземпляра - это объект, для которого вызывается * _eval.

Для class_eval область действия класса также становится целевым объектом, поэтому def создает методы экземпляра для этого класса / модуля.

Для instance_eval областью действия класса становится одноэлементный класс (также известный как метакласс, собственный класс) целевого объекта. Методы экземпляра, созданные в одноэлементном классе для объекта, становятся одноэлементными методами для этого объекта. Одноэлементные методы для класса или модуля - это то, что обычно (и несколько неточно) называется методами класса .

Область действия класса также используется для разрешения констант. Переменные класса ( @@ these @@ things ) разрешаются с помощью области действия класса, но они пропускают одноэлементные классы при поиске в цепочке вложенности модулей.

17
ответ дан 7 November 2019 в 07:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: