Быстрая функция swizzling/runtime
Перед Swift, в Objective-C, я бы swizzle или hook методы в классе, используя <objc/runtime.h>
.
Если у кого-то есть информация по теме изменения времени выполнения Swift и таких функций, как CydiaSubstrate и другие библиотеки, которые помогли в этой области, сообщите мне.
Ответы
Ответ 1
Мне удалось выполнить метод swizzling в Swift. В этом примере показано, как связать метод описания с NSDictionary
Моя реализация:
extension NSDictionary {
func myDescription() -> String!{
println("Description hooked")
return "Hooooked " + myDescription();
}
}
Swizzling code:
func swizzleEmAll() {
var dict:NSDictionary = ["SuperSecret": kSecValueRef]
var method: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("description"))
println(dict.description) // Check original description
var swizzledMethod: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("myDescription"))
method_exchangeImplementations(method, swizzledMethod)
println(dict.description) //Check that swizzling works
}
Отредактировано:
Этот код будет работать для любого пользовательского класса Swift, который наследуется от NSObject (но не будет работать для классов, которые этого не делают.) Дополнительные примеры - https://github.com/mbazaliy/MBSwizzler
Ответ 2
Вероятно, вы сможете легко swizzle-сгенерированные классы, которые наследуют классы Objective-C без проблем, поскольку они, похоже, постоянно используют динамическую отправку методов. Вы можете использовать swizzle методы классов с быстрым определением, которые существуют в среде выполнения Objective-C, поскольку они передаются через мост, но боковые методы Objective-C, скорее всего, будут просто прокси-серверами обратно через мост в swift- но не ясно, что было бы особенно полезно их подкачать.
"Чистые" вызовы быстрых методов не отображаются динамически с помощью чего-либо типа objc_msgSend
, и появляется (из кратких экспериментов), что безопасность типа swift реализована во время компиляции, и эта большая часть информации о текущем типе отсутствует (т.е. отсутствует) во время выполнения для неклассовых типов (оба из которых, вероятно, способствуют предполагаемым скоростным преимуществам быстрой).
По этим причинам я ожидаю, что осмысленные swizzling быстрые методы будут значительно сложнее, чем swizzling Objective-C методы, и, вероятно, будут выглядеть намного более похожими на mach_override
, чем метод w480 > swizzling.
Ответ 3
Я отвечаю на этот вопрос более года спустя, потому что ни один из других ответов не дает окончательного набора требований к методу swizzling для каждого класса.
То, что описано другим, в то время как оно будет безупречно работать для расширений классов основания /uikit (например, NSDictionary), будет просто никогда работать для ваших собственных классов Swift.
Как описано здесь, существует дополнительное требование к методу swizzling, отличному от расширения NSObject в вашем пользовательском классе.
Быстрый метод, по которому вы хотите swizzle , должен быть помечен dynamic
.
Если вы не отметите это, среда выполнения просто продолжит вызов исходного метода вместо swizzled, даже если указатели на методы, по-видимому, правильно меняются.
Обновление:
Я добавил этот ответ в сообщении в блоге.
Ответ 4
У меня был проект iOS Xcode 7, написанный в Swift 2, с использованием Cocoapods. В конкретном Cocoapod, с источником Objective-C, я хотел переопределить короткий метод, без наложения стручка. Написание расширения Swift не будет работать в моем случае.
Для использования swizzling метода я создал новый класс Objective-C в моем основном комплекте с методом, который я хотел бы заменить/вставить в cocoapod. (Также добавлен заголовок моста)
Используя mbazaliy решение по стеку потока, я поместил свой код, похожий на этот, в didFinishLaunchingWithOptions
в моем Appdelegate:
let mySelector: Selector = "nameOfMethodToReplace"
let method: Method = class_getInstanceMethod(SomeClassInAPod.self, mySelector)
let swizzledMethod: Method = class_getInstanceMethod(SomeOtherClass.self, mySelector)
method_exchangeImplementations(method, swizzledMethod)
Это сработало отлично. Разница между кодом @mbazaliy заключается в том, что мне не нужно было сначала создавать экземпляр класса SomeClassInAPod
, что в моем случае было бы невозможным.
Примечание. Я поместил код в Appdelegate, потому что каждый раз, когда код запускается, он обменивается методом для оригинала - он должен запускаться только один раз.
Мне также нужно было скопировать некоторые активы, которые были указаны в пакете Pod, в основной комплект.
Ответ 5
Я бы так не сделал, я думаю, что закрытие дает ответы (поскольку они дают вам возможность перехватывать, оценивать и перенаправлять вызов функции, кроме того, это будет легко расширить, когда и если у нас есть отражение.
http://www.swift-studies.com/blog/2014/7/13/method-swizzling-in-swift
Ответ 6
Я хотел бы расширить отличный ответ, предоставленный mbazaliy.
Еще один способ сделать swizzling в Swift - это реализовать реализацию с использованием блока Objective-C.
например. для замены метода description
на класс NSString
мы можем написать:
let originalMethod = class_getInstanceMethod(NSString.self, "description")
let impBlock : @objc_block () -> NSString =
{ () in return "Bit of a hack job!" }
let newMethodImp = imp_implementationWithBlock(unsafeBitCast(impBlock, AnyObject.self))
method_setImplementation(originalMethod, newMethodImp)
Это работает с Swift 1.1.
Ответ 7
Проведя некоторое время на этом... Проснитесь сегодня утром... бета-версия 6
Проблема Исправлена ошибка в beta6!
Из примечаний к выпуску
"Динамическая отправка теперь может вызывать переопределения методов и свойств, введенных в расширениях классов, фиксируя регрессию, введенную в Xcode 6 beta 5. (17985819)!"