runtime获取属性列表,修改属性值,添加并修改私有方法的调用

本文详细介绍了如何利用Objective-C的runtime机制获取并修改属性值,以及如何添加并修改私有方法。包括获取属性列表、修改属性值的具体步骤,特别是针对私有方法的修改进行了深入探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用了两天好好的研究了一下runtime,之前初涉runtime。懵懵懂懂的了解runtime的强大,但是如何的强大,只是在demo中引用MJExtension的时候自动生成model,了解了一些。
经过一天半的研究,首先温习了之前的获取属性列表。

今天想说的具体功能如题所述。主要涉及的就是获取并修改属性。添加并修改私有方法。

好了,首先说一下如何获取属性值。如果需要获取某个属性,我们最先需要知道你想获取的类的名字。如果我们只有某一个类的字符串,同样可以达到目的,这也就是runtime的神奇之处。

(1)例如我们想要得到UserInfo这个类下的所有属性。但是现在只有字符串className=@"UserInfo",我们可以用最常见的runtime语句去获取到class。如下:Class UserInfo = NSClassFromString(className);这时候我们就得到了这个类。但是这时的UserInfo相当于没有alloc的类。

(2)我们在网上有两种方式可以获取到属性的name,type以及value。第一种方式如下:

- (NSArray*)getAllPropertyName//runtime获取model所有属性
{
    NSMutableArray* nameArray = [NSMutableArray array];
    unsigned int count = 0;
    objc_property_t *property_t = class_copyPropertyList([UserInfo class], &count);
    for (int i=0; i<count; i++) {
        objc_property_t propert = property_t[i];
        const char * propertyName = property_getName(propert);
        [nameArray addObject:[NSString stringWithUTF8String:propertyName]];
    }
    free(property_t);
    return nameArray;
}

上面只是获取到属性名,如果想要获取其它的东西,也可以自行查询方法。不过目前只有这个有点用。
第二种方法如下:

    unsigned int countNum = 0;
    Ivar *members = class_copyIvarList([self.panoramView class], &countNum);
    for (int i=0; i<countNum; i++) {
        Ivar var = members[i];
        const char *memberName = ivar_getName(var);
        //属性描述
        NSString *memberStr = [NSString stringWithFormat:@"%s",memberName];
        if ([memberStr isEqualToString:@"_advsObject"]) {
            const char * type = ivar_getTypeEncoding(var);
        }
    }

上面的方法获取到的属性是C的格式,需要转变成OC,所以中间写了一个memberStr作为媒介。

以上就是获取属性的方法,下面说下如何修改属性内容。

(3)其实修改属性的方法,很简单,就一句话,一共有三个参数。object_setIvar(id obj, Ivar ivar, id value)
*@param obj The object containing the instance variable whose value you want to set.
* @param ivar The Ivar describing the instance variable whose value you want to set.
* @param value The new value for the instance variable.*这是官方给的解释。

我们根据以上的三个参数,对其进行赋值,如下:object_setIvar(self.userInfo, var, @"改过的内容");
到此:属性的修改就结束了。今天写这篇文章,主要是为了记录如何修改私有方法的。所以接下来才是重头戏。

如果我们要修改某一个类库的私有方法,当然,通过runtime都是为了修改私有方法的,毕竟非私有方法,又何必用runtime这个繁琐的东西呢。我这里的需求说起来还是被第三方深恶痛绝的,但是对一些技术还处在一个瓶颈,无法越过的同学们是一个福音。
我的需求就是,下载的第三方,为了防止我们滥用他们的劳动成果,也或者为了获得一定的报酬,所以才在代码中封装一个明显的lable或者context等控件悬浮在view上。我就是要把他悬浮的这个控件屏蔽掉,或者隐藏或者remove掉,期初想用KVO,直接setvalue:hidden的,但是由于这个控件是私有变量,所以无法直接使用KVO访问的。最终选择了使用runtime修改私有方法即可。

(4)同样我们需要知道要修改的类的名字,然后获取该类下的所有方法。代码如下:

-(NSArray *)getAllMethods
{
    unsigned int count_f =0;
    //获取方法链表
    Method* methodList_f = class_copyMethodList([UserInfo class],&count_f);

    NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:count_f];

    for(int i=0;i<count_f;i++)
    {
        Method temp_f = methodList_f[i];
        //方法的调用地址
        IMP imp_f = method_getImplementation(temp_f);
        //方法
        SEL name_f = method_getName(temp_f);
        //方法名字符串
        const char* name_s =sel_getName(method_getName(temp_f));
        //参数数量
        int arguments = method_getNumberOfArguments(temp_f);
        //返回方法的参数和返回值的描述的字串
        const char* encoding =method_getTypeEncoding(temp_f);
        NSLog(@"方法名:%@,参数个数:%d,编码方式:%@",[NSString stringWithUTF8String:name_s],
              arguments,[NSString stringWithUTF8String:encoding]);

        NSString *methodStr = NSStringFromSelector(name_f);

        [methodsArray addObject:methodStr];

    }
    free(methodList_f);

    return methodsArray;
}

以上就是获取的该类下的所有方法,这个时候我们就该猜了,这是一个痛苦的过程,因为我们不知道到底哪个方法是我们需要修改的。但是一般开发者命名的时候是会根据需要做的意思而命名的,我们只能这样缩小范围了,但是如果真的碰到了加密的方法,或者说命名方法没有规律的,那我真的没办法了。只能一个一个试试了。

(5)如果我们搜索到了方法名为:setUserName:我们需要添加方法,然后再修改这个私有方法的回调。

-(void)laodNewMethod
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [UserInfo class];
        //原来的方法
        SEL originalSelector = @selector(setUserName:);
        //新方法
        SEL swizzledSelector = @selector(setNewUserName:);
        //原来的Method
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        //新Method
        Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
        //添加到runtime新方法
        BOOL didAddMethod =class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
        if (didAddMethod) {
            class_replaceMethod([self class],swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }


    });
}
-(void)setNewUserName:(NSString *)newUserName
{
    NSlog("这里就是修改后的方法,每次运行都会自动跳到这里调用这个方法了。")
}

以上就是所有今天关于runtime的内容

<think>好的,用户的问题是关于在Spring Boot中使用反射获取字段注解的示例。我需要先理清楚用户的需求,然后结合提供的引用内容来构建答案。 首先,用户可能是在开发Spring Boot应用时,遇到了需要动态处理字段注解的情况。例如,自定义注解,然后通过反射读取注解的,进行一些业务逻辑处理,比如数据校验、动态配置等。根据引用[1],Spring本身大量使用反射来管理Bean,比如动态创建实例、依赖注入和AOP。因此,用户的问题属于如何在自己的代码中利用反射机制,类似于Spring容器内部的操作。 接下来,我需要回忆反射的基本步骤。获取字段的注解通常涉及以下几个步骤:获取类的Class对象,通过Class对象获取字段,然后从字段上获取注解实例,最后读取注解的属性值。同时,需要处理可能的异常,比如NoSuchFieldException,以及确保注解的保留策略是RUNTIME,这样才能在运行时通过反射获取,如引用[4]中的@JasyptField注解,确实使用了@Retention(RetentionPolicy.RUNTIME),这样是正确的。 然后,结合用户提供的引用内容,特别是引用[4]中的自定义注解示例,可以构建一个类似的例子。比如,用户可能定义了一个注解,比如@CustomAnnotation,然后需要读取该注解的。需要确保在示例代码中正确使用反射API,如getDeclaredField获取字段,再getAnnotation获取注解实例。 另外,考虑到Spring Boot的环境,可能需要获取Spring管理的Bean中的字段注解,这时候可能需要结合ApplicationContext来获取Bean实例,再对其进行反射操作。不过用户的问题更偏向于普通的反射操作,不需要涉及Spring容器获取Bean的过程,所以示例中可以简化,直接使用一个普通对象。 还需要考虑代码的完整性,比如异常处理,是否需要遍历所有字段,或者如何集成到Spring Boot的服务中。例如,可能在一个服务类中编写工具方法,处理特定对象的字段注解。 最后,根据用户提供的引用[3],Spring Boot的参数校验使用@Valid和异常处理,可能在示例中提到类似的应用场景,比如自定义校验注解,通过反射读取注解进行校验,但用户的问题更侧重于如何获取注解,而不是校验本身,所以示例应重点展示反射获取的过程。 总结步骤: 1. 创建自定义注解,确保保留策略为RUNTIME。 2. 在实体类的字段上使用该注解。 3. 编写工具类方法,使用反射获取字段的注解实例。 4. 读取注解的属性值处理可能的异常。 需要检查示例代码是否符合这些步骤,确保语法正确。同时,参考引用中的代码结构,比如引用[4]的注解定义,引用[1]中提到的反射应用场景,确保回答的准确性和相关性。</think>在Spring Boot中,通过反射获取字段注解的实现步骤如下(结合代码示例说明): --- ### 1. 定义自定义注解 首先创建作用在字段上的注解,注意必须设置`@Retention(RetentionPolicy.RUNTIME)`以保证运行时可见: ```java @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomAnnotation { String value() default ""; int priority() default 0; } ``` --- ### 2. 在实体类中使用注解 在需要处理的字段上添加注解: ```java public class User { @CustomAnnotation(value = "username", priority = 1) private String name; @CustomAnnotation(value = "userAge", priority = 2) private int age; } ``` --- ### 3. 反射获取注解 通过反射API获取字段及其注解信息: ```java public class AnnotationProcessor { public static void processAnnotations(Object obj) { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(CustomAnnotation.class)) { CustomAnnotation annotation = field.getAnnotation(CustomAnnotation.class); System.out.println("字段名: " + field.getName()); System.out.println("注解: " + annotation.value()); System.out.println("优先级: " + annotation.priority()); } } } } ``` --- ### 4. 在Spring Boot中集成使用 结合Spring容器调用示例: ```java @Service public class UserService { public void validateUser(User user) { AnnotationProcessor.processAnnotations(user); // 触发注解处理 } } ``` --- ### 关键点解析 - **反射操作入口**:通过`Class.getDeclaredFields()`获取所有字段,遍历后检查注解存在性 - **注解生命周期**:必须使用`@Retention(RetentionPolicy.RUNTIME)`才能使注解在运行时可见[^4] - **访问私有字段**:若需要访问私有字段,需调用`field.setAccessible(true)` - **性能优化**:反射操作成本较高,建议缓存`Class`对象和`Field`信息 --- ### 典型应用场景 1. **动态配置解析**:根据注解生成字段映射关系 2. **数据校验扩展**:自定义校验规则注解(类似@Valid的扩展实现)[^3] 3. **序列化控制**:根据注解决定字段序列化方式 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值