Быстрая функция 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)!"