首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Swizzling UIImage.init(named:)在获取实例方法时返回nil

基础概念

UIImage 是 iOS 开发中用于表示图像的类。init(named:)UIImage 的一个便利初始化器,用于从应用程序的主 bundle 中加载图像。Swizzling 是一种运行时技术,用于替换类的方法实现。

问题描述

在使用 Swizzling 技术时,尝试获取 UIImageinit(named:) 方法的实例方法,但返回 nil

原因分析

  1. 方法不存在init(named:) 是一个便利初始化器,而不是实例方法。便利初始化器在类的元类(metaclass)中定义,而不是在实例方法列表中。
  2. Swizzling 时机不对:如果在类加载完成后再进行 Swizzling,可能会导致获取不到方法。
  3. 类已经被加载:如果类已经被加载并且初始化器已经被替换,再次尝试获取可能会失败。

解决方案

  1. 使用正确的类和元类
    • UIImage 的便利初始化器在元类中定义,因此需要获取元类的方法。
    • 使用 class_getClassMethod 而不是 class_getInstanceMethod
  • 确保在类加载前进行 Swizzling
    • 可以在 +load 方法中进行 Swizzling,因为 +load 方法会在类加载时调用。

示例代码

代码语言:txt
复制
import UIKit

extension UIImage {
    static func swizzleInitNamed() {
        guard let originalSelector = #selector(UIImage.init(named:)),
              let swizzledSelector = #selector(swizzledInitNamed(named:)) else {
            return
        }
        
        let originalMethod = class_getClassMethod(UIImage.self, originalSelector)
        let swizzledMethod = class_getClassMethod(UIImage.self, swizzledSelector)
        
        if originalMethod != nil && swizzledMethod != nil {
            method_exchangeImplementations(originalMethod!, swizzledMethod!)
        }
    }
    
    @objc private static func swizzledInitNamed(named name: String?) -> UIImage? {
        // 自定义逻辑
        print("Swizzled init(named:) called with \(name ?? "")")
        return swizzledInitNamed(named: name)
    }
}

// 在 +load 方法中进行 Swizzling
+load {
    UIImage.swizzleInitNamed()
}

参考链接

通过上述方法,可以在类加载时正确地 Swizzle UIImageinit(named:) 方法,并确保获取到正确的元类方法。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

  • 教你如何自定义AlertView

    本文就介绍如何自定义alertView,看完你就懂得制作属于自己的alertView了 一、创建DWAlert.swift 创建一个类名为DWAlert.swift,class DWAlert:...(named: "1.png"), for: .normal) cancleBtn.setImage(UIImage.init(named: "2.png"), for: .highlighted...方法中添加的backImageView背景蒙版 2、获取当前主窗口,并定义一个alertView的frame 3、利用UIView.animate对alertView进行动画操作。...里面实现,该方法会在当alertView即将加入主窗口被系统自动调用,详情请看UIView不可不知的秘密 override func willMove(toSuperview newSuperview...五、使用DWAlert ViewController创建一个按钮,并添加一个点击事件ClickMe,方法里面创建alertView @IBAction func ClickMe(_ sender:

    1.3K50

    RuntimeiOS开发中的实际应用

    1.2 相关函数 //为一个实例对象添加一个关联对象,由于是C函数只能使用C字符串,这个key就是关联对象的名称,value为具体的关联对象的值,policy为关联对象策略,与我们自定义属性设置的修饰符类似...以至于苹果发邮件禁止使用热修复 整个JSPath的Issues被炸锅了。热修复主要做的是替换现有的方法,或者增加新方法,需要对消息发送和转发有一定的理解。...如果获取到,则直接转发给它。如果返回nil,继续下面的动作。...如果 -methodSignatureForSelector: 返回 nil ,Runtime 则会发出 -doesNotRecognizeSelector: 消息,程序这时也就挂掉了。...处理用户登录 5.4Crash的防范 OC中容器类空值nil 和数组越界都会直接导致我们app 的crash 我们一种处理方式是利用Category增加新方法中判断值是否为空或者越界,对于新工程我们使用大家约定使用容器的

    1K20

    OC对象模型

    ; } 从上面可以看到,class类方法实例方法都是获取当前Class也就是isa指针 实例方法调用时,通过对象的 isa 类中获取方法的实现 类方法调用时,通过类的 isa 元类中获取方法的实现...当某个类的对象第一次被观察,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。然后派生类的setter方法里实现通知机制。...简单而言:实例对象在被观察,生成派生类,派生类setter方法中valuewillchange方法和valuesdidchanged方法里发出通知,并且通过isa-swizzling,从而使实例对象成为派生类的对象...,所以实例对象setter属性可以产生通知。...(NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES) 3、如果没有找到成员变量,调用setValue:forUnderfinedKey: 获取值 valueForKey

    65920

    iOS_Runtime是什么?原理?作用?怎么实现weak?使用

    就是程序运行的过程中,有一套C语言级别的API,它把代码从OC转换成C 2、原理: OC是基于C,并添加了面向对象的特性,将很多静态语言在编译和链接做的事放到了runtime运行时来处理 C:函数的调用在编译就知道会调用哪个函数...OC:在编译的时候并不知道,只正在运行时才会根据函数名称找到对应的函数 3、作用 获取属性、方法、成员变量、协议(包括私有的) 给分类动态添加属性、方法 字典转模型 拦截并替换方法 实现NSCoding...当对象的引用计数为0会调用dealloc方法,此时会在weak表中搜索,将所有weak对象置为nil。...Key:对象内存地址 — value:n个weak对象 5、使用 替换ViewController生命周期方法 解决获取索引、添加、删除元素越界crash问题 防止按钮重复暴力点击 全局更换控件初始效果...App热修复 App异常加载占位图通用类封装 全局修改导航栏返回按钮 (去掉title) 以下是使用内容: `NSObject`的`Category`里实现方法替换,方便需要的类直接调用: // NSObject

    38120

    Objective-C Runtime详解

    实际上,它是方法实现中访问消息接收者对象的实例变量的途径 而当方法中的 super 关键字接收到消息,编译器会创建一个 objc_super 结构体: struct objc_super { id...获取方法地址 IMP 那节提到过可以避开消息绑定而直接获取方法的地址并调用方法。这种做法很少用,除非是需要持续大量重复调用某方法的极端情况,避开消息发送泛滥而直接调用该方法会更高效。...如果此方法返回 nil 或 self,则会进入消息转发机制(forwardInvocation:);否则将向返回的对象重新发送消息。 转发 当动态方法解析不作处理返回NO,消息转发机制会被触发。...当一个类被编译实例变量的布局也就形成了,它表明访问类的实例变量的位置。...健壮的实例变量下编译器生成的实例变量布局跟以前一样,但是当 runtime 系统检测到与超类有部分重叠它会调整你新添加的实例变量的位移,那样你子类中新添加的成员就被保护起来了 需要注意的是健壮的实例变量下

    1.7K60

    Objective-C Runtime 详解

    实际上,它是方法实现中访问消息接收者对象的实例变量的途径 而当方法中的 super 关键字接收到消息,编译器会创建一个 objc_super 结构体: struct objc_super { id...获取方法地址 IMP 那节提到过可以避开消息绑定而直接获取方法的地址并调用方法。这种做法很少用,除非是需要持续大量重复调用某方法的极端情况,避开消息发送泛滥而直接调用该方法会更高效。...self,因为那样会死循环 转发 当动态方法解析不作处理返回NO,消息转发机制会被触发。...当一个类被编译实例变量的布局也就形成了,它表明访问类的实例变量的位置。...健壮的实例变量下编译器生成的实例变量布局跟以前一样,但是当 runtime 系统检测到与超类有部分重叠它会调整你新添加的实例变量的位移,那样你子类中新添加的成员就被保护起来了 需要注意的是健壮的实例变量下

    1.2K20

    Method-Swizzling 方法交换

    method-swizzling的含义是方法交换,其主要作用是在运行时将一个方法的实现替换成另一个方法的实现,这就是我们常说的iOS黑魔法, OC中就是利用method-swizzling实现AOP,...生成对应关系 如下图所示,交换前后的sel和IMP的对应关系 method-swizzling涉及的相关API 通过sel获取方法Method class_getInstanceMethod:获取实例方法...oriMethod) { // oriMethod为nil,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下: class_addMethod(cls...方法获取方法 调用class_addMethod和class_replaceMethod方法添加和替换,需要传入的类是元类,元类可以通过object_getClass方法获取类的元类 //封装的method-swizzling...oriMethod) { // 避免动作没有意义 // oriMethod为nil,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下: class_addMethod

    64140

    Objc Runtime 总结

    struct objc_object *id; 向object发送消息,Runtime库会根据object的isa指针找到这个实例object所属于的类,然后类的方法列表以及父类方法列表寻找对应的方法运行...如果有同名会返回NO,修改的话需要使用method_setImplementation // 获取实例方法 Method class_getInstanceMethod ( Class cls, SEL...Method中的接收消息对象参数和方法选择器参数 Method中使用self关键字来引用实例本身,self的内容即接收消息的对象是Method运行时被传入的同时还有方法选择器。...注意的是forwardInvocation:方法只有消息接收对象无法正常响应消息才被调用。...前面的消息转发很强大,但是需要能够修改对应类的源码,但是对于有些类无法修改其源码又要更改其方法实现时可以使用Method Swizzling,通过重新映射方法来达到目的,但是跟消息转发比起来调试会困难

    75620

    Runtime系列(二)--Runtime的使用场景

    1.3获取某个类的实例变量 如果你还需要获取某个类的实例变量做什么操作的话,可以使用如下这几个API: // 获取实例变量数组 Ivar * class_copyIvarList(Class cls,...unsigned int *outCount) // 获取实例变量名称 const char * ivar_getName( Ivar ivar) // 获取实例变量类型 const char * ivar_getTypeEncoding...不能添加属性的根本原因是不会帮我们自动添加对象的实例变量,也不会帮我们生成set 和get方法,虽然set /get 方法可以自己实现,但是没有实例变量来存储数据。 ?...补充一个关联对象的使用场景: 你使用AlertView 或者ActionSheet的时候,有没有很苦恼不能在点击的代理方法中方便的获取到Model对象呢?...除了控制器中添加一个property 这种方式外; 我们也可以为AlertView 或者ActionSheet 添加一个关联对象,这样就可以代理方法中方便的获取到Model 对象啦。

    1.6K42

    iOS 开发:『Crash 防护系统』(一)Unrecognized Selector

    Method Swizzling 方法的封装 由于这几种常见 Crash 的防护都需要用到 Method Swizzling 技术。...Unrecognized Selector 防护 4.1 unrecognized selector sent to instance(找不到对象方法的实现) 如果被调用的对象方法没有实现,那么程序在运行中调用该方法...如果这一步方法返回 nil,则进入下一步。 消息重定向:Runtime 系统利用 methodSignatureForSelector: 方法获取函数的参数和返回值类型。...如果 methodSignatureForSelector: 返回 nil。则 Runtime 系统会发出 doesNotRecognizeSelector: 消息,程序也就崩溃了。...把消息转发给动态生成类的实例对象,由目标类动态创建的方法实现,这样 APP 就不会崩溃了。

    2.1K30

    iOS基础理论(三)

    b.调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回nil 对象。否则返回 nil ,继续下面的动作。...如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回nil:创建一个 NSlnvocation 并传给forwardInvocation:。...d.调用forwardInvocation:方法,将第3步获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。...答案是通过 isa 混写(isa-swizzling)。 23、若一个类有实例变量NSString *_foo,调用setValue:forKey:,可以以foo还是_foo作为key? 都可以。...然而 KVO 实现中使用了isa 混写( isa-swizzling),这个的确不是很容易发现:Apple 还重写、覆盖了-class方法返回原来的类。

    57730

    iOS开发·runtime原理与实践: 方法交换篇(Method Swizzling)(iOS“黑魔法”,埋点统计,禁止UI控件连续点击,防奔溃处理)

    用法 先给要替换的方法的类添加一个Category,然后Category中的+(void)load方法中添加Method Swizzling方法,我们用来替换的方法也写在这个Category中。...注意要点 Swizzling应该总在+load中执行 Swizzling应该总是dispatch_once中执行 Swizzling+load中执行时,不要调用[super load]。...思路:对NSArray的objectAtIndex:方法进行Swizzling,替换一个有处理逻辑的方法。但是,这时候还是有个问题,就是类簇的Swizzling没有那么简单。...所以如果想对NSArray进行Swizzling,必须获取到其“真身”进行Swizzling,直接对NSArray进行操作是无效的。...所以如果我们对NSArray类进行Swizzling操作其实只是对父类进行了操作,NSArray内部会创建其他子类来执行操作,真正执行Swizzling操作的并不是NSArray自身,所以我们应该对其

    2.6K60

    RunTime 之Method Swizzling

    Objective-C 提供了以下 API 来动态替换类方法实例方法的实现: class_replaceMethod 替换类方法的定义 method_exchangeImplementations...当类中没有想替换的原方法,该方法会调用class_addMethod来为该类增加一个新方法,也因为如此,class_replaceMethod调用时需要传入types参数,而method_exchangeImplementations...method_setImplementation 最简单的用法,当仅仅需要为一个方法设置其实现方式使用。 以上 3 个方法的源码 这里,感兴趣的同学可以读一读。...应用一:拦截系统自带的方法调用Method Swizzling 一般是load方法中进行,确保最先被调用。+load方法会在Appdelegate的方法之前执行,是最先执行的方法。...使用场景 Method Swizzling 可以重写某个方法而不用继承,同时还可以调用原先的实现。通常的做法是category中添加一个方法(当然也可以是一个全新的class)。

    1.4K31
    领券