技术标签: iOS
Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的确定变量类型,执行变量类型对应的方法的。因此,在Object-C中常用字符串映射类的技巧来动态创建类对象。因为OC的动态语言特性,我们可以通过一些手段,在程序运行时动态的更改对象的变量甚至方法,这就是我们所说的runtime机制。
首先,我们先来看一个例子,这里有我创建的一个MyObject类:
//.h===========================
@interface MyObject : NSObject
{
@private
int privateOne;
NSString * privateTow;;
}
@end
//=============================
//.m===========================
@interface MyObject()
{
@private
NSString * privateThree;
}
@end
@implementation MyObject
- (instancetype)init
{
self = [super init];
if (self) {
privateOne=1;
privateTow=@"Tow";
privateThree=@"Three";
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"one=%d\ntow=%@\nthree=%@\n",privateOne,privateTow,privateThree];
}
@end
//=============================
这个类是相当的安全,首先,在头文件中没有提供任何的方法接口,我们没有办法使用点语法做任何操作,privateOne和PrivateTow两个变量虽然声明在了头文件中,却是私有类型的,通过指针的方式我们虽然可以看到他们,却不能做任何读取修改的操作,xcode中的提示如下:
他会告诉我们,这是一个私有的变量,我们不能使用。对于privateThree,我们更是束手无策,不仅不能使用,我们甚至都看不到它的存在。那么对于这种情况,你有什么办法操作这些变量么?对,是时候展现真正的技术了:runtime!
要操作对象的变量,我们首先应该要捕获这些变量,让他们无处遁形。无论声明在头文件或是实现文件,无论类型是公开的还是私有的,只要声明了这个变量,系统就会为其分配空间,我们就可以通过runtime机制捕获到它,代码如下:
#import "ViewController.h"
#import "MyObject.h"
//包含runtime头文件
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//我们先声明一个unsigned int型的指针,并为其分配内存
unsigned int * count = malloc(sizeof(unsigned int));
//调用runtime的方法
//Ivar:方法返回的对象内容对象,这里将返回一个Ivar类型的指针
//class_copyIvarList方法可以捕获到类的所有变量,将变量的数量存在一个unsigned int的指针中
Ivar * mem = class_copyIvarList([MyObject class], count);
//进行遍历
for (int i=0; i< *count ; i++) {
//通过移动指针进行遍历
Ivar var = * (mem+i);
//获取变量的名称
const char * name = ivar_getName(var);
//获取变量的类型
const char * type = ivar_getTypeEncoding(var);
NSLog(@"%s:%s\n",name,type);
}
//释放内存
free(count);
//注意处理野指针
count=nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果如下,其中i表示int型:
是不是小吃惊了一下,无论变量在哪里,只要它在,就让它无处遁形。
仅仅能够获得变量的类型和名字或许并没有什么卵用,没错,我们获取变量的目的不是为了观赏,而是为了操作它,这对runtime来说,也是小事一碟。代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
//获取变量
unsigned int count;
Ivar * mem = class_copyIvarList([MyObject class],&count);
//创建对象
MyObject * obj = [[MyObject alloc]init];
NSLog(@"before runtime operate:%@",obj);
//进行变量的设置
object_setIvar(obj, mem[0],10);
object_setIvar(obj, mem[1], @"isTow");
object_setIvar(obj, mem[2], @"isThree");
NSLog(@"after runtime operate:%@",obj);
}
Tip:在修改int型变量的时候,你或许会遇到一个问题,ARC下,编译器不允许你将int类型的值赋值给id,在buildset中将Objective-C Automatic Reference Counting修改为No即可。
打印效果如下:
可以看到,那些看似非常安全的变量被我们修改了。
变量通过runtime机制我们可以取到和改变值,那么我们再大胆一点,试试那些私有的方法,首先我们在MyObject类中添加一些方法,我们只实现,并不声明他们:
@interface MyObject()
{
@private
NSString * privateThree;
}
@end
@implementation MyObject
- (instancetype)init
{
self = [super init];
if (self) {
privateOne=1;
privateTow=@"Tow";
privateThree=@"Three";
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"one=%d\ntow=%@\nthree=%@\n",privateOne,privateTow,privateThree];
}
-(NSString *)method1{
return @"method1";
}
-(NSString *)method2{
return @"method2";
}
这样的方法我们在外面是无法调用他们的,和操作变量的思路一样,我们先要捕获这些方法:
//获取所有成员方法
Method * mem = class_copyMethodList([MyObject class], &count);
//遍历
for(int i=0;i<count;i++){
SEL name = method_getName(mem[i]);
NSString * method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"%@\n",method);
}
打印如下:
得到了这些方法名,我们大胆的调用即可:
MyObject * obj = [[MyObject alloc]init];
NSLog(@"%@",[obj method1]);
Tip:这里编译器不会给我们方法提示,放心大胆的调用即可。
这个runtime机制最强大的部分要到了,试想,如果我们可以动态的向类中添加方法,那将是一件多么令人激动的事情,注意,这里是动态的添加,和类别的最大不同在于这种方式是运行时才决定是否添加方法的。
- (void)viewDidLoad {
[super viewDidLoad];
//添加一个新的方法,第三个参数是返回值的类型v是void,i是int,:是SEL,对象是@等
class_addMethod([MyObject class], @selector(method3), (IMP)logHAHA, "v");
unsigned int count = 0;
Method * mem = class_copyMethodList([MyObject class], &count);
for(int i=0;i<count;i++){
SEL name = method_getName(mem[i]);
NSString * method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"%@\n",method);
}
MyObject * obj = [[MyObject alloc]init];
//运行这个方法
[obj performSelector:@selector(method3)];
}
//方法的实现
void logHAHA(){
NSLog(@"HAHA");
}
运行结果如下:
从前五行可以看出,方法已经加进去了,从最后一行可以看出,执行没有问题。
程序员总是得寸进尺的,现在,我们要做点事情,用我们的函数替换掉类中的函数:
- (void)viewDidLoad {
[super viewDidLoad];
MyObject * obj = [[MyObject alloc]init];
//替换之前的方法
NSLog(@"%@", [obj method1]);
//替换
class_replaceMethod([MyObject class], @selector(method1), (IMP)logHAHA, "v");
[obj method1];
}
void logHAHA(){
NSLog(@"HAHA");
}
打印如下:
这次够cool吧,通过这个方法,我们可以把系统的函数都搞乱套。当然,runtime还有许多很cool的方法:
id object_copy(id obj, size_t size)
拷贝一个对象
id object_dispose(id obj)
释放一个对象
const char *object_getClassName(id obj)
获取对象的类名
ive
void method_exchangeImplementations(Method m1, Method m2)
交换两个方法的实现
专注技术,热爱生活,交流技术,也做朋友。
——珲少 QQ群:203317592
文章浏览阅读1.7k次。GT06_traccar支持的设备
文章浏览阅读9.7k次,点赞2次,收藏2次。在《修改netfilter的limit模块实现基于单个ip的流量监控》中,介绍了一种方式实现针对一个网段每个IP地址的流量控制,如果细化到流,那个就叫做针对每个流的流量控制,我们知道,一个IP地址可以和很多流相关联,针对流的流控限制的不是主机,而是主机上的一个连接,它的约束要比针对IP地址的流控更加小。 然而如何来实现这个呢?实际上在Linux中,几乎所有的流控都可以用TC工具配置出_netfilter限速
文章浏览阅读1.8k次。原文链接:java获取指定文件夹下的所有文件名_tomorrowzm的专栏-程序员宅基地_java查询指定文件夹下的所有文件输出文件名 site:blog.csdn.netblog.csdn.netpackage 这里我们主要使用的是listFiles函数来得到file文件夹下的所有文件,包括文件夹。然后通过File类的isFile和isDirectory来区分,如果是文件,就输出对应的信息,如..._java读取linux服务器下指定目录下的文件名称
文章浏览阅读7.6k次,点赞7次,收藏20次。文章目录前言一、环境开发环境测试环境二、灵光乍现MyBatis-Plus源码2.初见真正的批量更新语法三、开工基础类搭建SysUser(表sys_user实体类)Stash(拼接SQL服务,内部类)TableCacheDTO(数据表信息存储)TableCache(表信息缓存)MySQL拼接常量类缓存数据库表信息1. 继承AbstractMethod2. 自定义sql注入器3. 自定义注入器生效事务工具类制作SQL工具类SQL执行类四、测试100条测试数据1千条测试数据1万条测试数据10万条测试数据五、弊端总_批量update
文章浏览阅读2.2k次,点赞4次,收藏6次。作为PID系列专题,这些文章,我都会给出PLC梯形图的源代码和SCL代码方便大家对比学习,文章中的错误和不严谨之处,也请大家指正。1、专题1:设定值响应问题 2、PLC的梯形图代码,这部分我们可以做成功能块,启用PID运算时,我们可以对设定值进行线性化平滑处理,也可以不处理。......_控制斜坡pid
文章浏览阅读1w次,点赞3次,收藏4次。36进制转换成10进制的方法,以R9和10Y为例R9就是 27 * 36^1 + 9*36^0 = 98110Y 就是 1* 36^2 + 0 * 36^1 + 34*36^0 =133010进制转换成36进制的方法,以1079和52360为例(1079/36^0) % 36 = 35(1079/36^1) % 36 = 29(1079/36^2) 所以_36进制转换10进制
文章浏览阅读7.5k次。MetasploitMetasploit Framework简称MSF(ruby语言开发的)实验环境准备Metasploit的使用第一次使用要进行数据库的初始化msfdb init用的时候就:msfconsole也可以:msfdb run (就等于msfdb init 和msfconsole)Metasploit指令search ms17_010 会列出许多模..._msf war包部署 渗透
文章浏览阅读1.4w次,点赞11次,收藏89次。将cookies添加到requests.session中实现淘宝的模拟登录声明:本文仅供学习用,旨在分享我们知道现在爬取淘宝商品是必须要登录的,在没有登录的情况下搜索商品也会自动重定向到登录页面。之前学着用selenium,pyppeteer等自动化框架模拟登录淘宝,但是无论怎么滑动滑块验证都失败。然而就像星爷《新喜剧之王》中所说得:只要不投降就是成功,同时为了安慰自己受伤的小心灵,决定用co..._浏览器登录淘宝后使用其cookie再用requests登录
文章浏览阅读1.6w次,点赞2次,收藏68次。定义wiki关于lda的定义:隐含狄利克雷分布简称LDA(Latent Dirichlet allocation),是一种主题模型,它可以将文档集中每篇文档的主题按照概率分布的形式给出。同时它是一种无监督学习算法,在训练时不需要手工标注的训练集,需要的仅仅是文档集以及指定主题的数量k即可。此外LDA的另一个优点则是,对于每一个主题均可找出一些词语来描述它。LDA首先由Blei, David M.、_lda关键词提取怎么和文本进行相似度
文章浏览阅读80次。一般在python进行数据分析/统计分析时,第一步总是对数据进行一些描述性分析、相关性分析,但是总会是有一大堆代码,那么今天就介绍一个神器pandas_profiling,一行命令就能搞定大部分描述性分析!安装pip install pandas_profiling使用那么我们继续使用之前文章中使用过很多次的NBA数据集,还记得我们在介绍pandas使用的那篇文章中分很多章节去讲解如何使用pand..._python生成数据分析报告
文章浏览阅读415次,点赞10次,收藏9次。复制一个这个模块,把原来的模块删除,解决了。_has unapplied changes. please apply or cancel these changes before running t
文章浏览阅读2.4k次,点赞4次,收藏10次。当不想使用一款软件的时候,大部分人都会选择在控制面板里直接卸载它,通常这么做是没什么问题的。然而许多流氓软件可不会轻易地就甘心从你的电脑里消失,它们不想着尽力完善本身应该实现的功能,却把重心都放在了怎么压榨你的电脑资源,怎么给你弹更多的广告,怎么捆绑更多的组件,在你想要卸载时还要可怜兮兮的恳求留下它。(国产大部分视频软件客户端)更过分的还有直接卸载不干净的,无论怎么通过正常的途径去卸载,..._cad uninstall tool