文档章节

iOS 运行时runtime控制私有变量以及私有方法

hanbing94
 hanbing94
发布于 2017/05/07 14:22
字数 1890
阅读 12
收藏 0
点赞 0
评论 0

OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类、对象中的所有属性、方法,就算是私有方法以及私有属性都是可以动态修改的。本文旨在对runtime的部分特性小试牛刀,更多更全的方法可以参考系统API文件<objc/runtime.h>。

先看一个非常平常的Father类:

1

2

3

4

5

#import <Foundation/Foundation.h>

 

@interface Father : NSObject

@property (nonatomic, assign) int age;

@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

#import "Father.h"

 

@interface Father ()

{<br>  NSString *_name;<br>}<br>

- (void)sayHello;

 

@end

 

@implementation Father

 

- (id)init

{

    if (self = [super init]) {

        _name = @"wengzilin";

        [_name copy];

        self.age = 27;

    }

    return self;

}

- (void)dealloc

{

    [_name release];

    _name = nil;

    [super dealloc];

}

- (NSString *)description

{

    return [NSString stringWithFormat:@"name:%@, age:%d", _name, self.age];

}

- (void)sayHello

{

    NSLog(@"%@ says hello to you!", _name);

}

- (void)sayGoodbay

{

    NSLog(@"%@ says goodbya to you!", _name);

}

如果你没接触过runtime,那当我问你:“Father之外的类能控制的属性有哪些?能控制的方法有哪些?”时,你估计会回答:“我们可以访问age属性,不能访问_name变量;可以访问age的setter/getter方法,其他方法都不行”。这种回答是OK的,因为教科书上以及面向对象的思想告诉我们,事实如此。但是,我会说,有一种方法是APPLE允许的而且可以不受这些规则限制的途径可以做到想访问什么就访问什么、想修改什么就修改什么,那就是本文的主题:RUNTIME!

现在我们简单地将本文的主题分为两部分:(1)控制私有变量  (2)控制私有函数,因为二者所用的runtime差异较大,函数部分会复杂一些

(1)控制变量

想要控制一个类的私有变量,那第一步就要知道这个类到底有哪些隐藏的变量,以及这些隐藏的变量类型是什么。或许你会说:“这不是很显然吗?.h文件都写着呢!”。如果你真这么想就特错特错了,很多正规的写法都是尽量避免在.h文件中出现私有变量,绝大部分都会选择方法.m文件的extension中,extension就是匿名的category。我猜测这也是一种防止hack的措施吧。不管这些变量放在何处,runtime都可以让他们无所遁形!先看代码,看不懂不要紧,后面会有解释:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

- (void)tryMember

{

    Father *father = [[Father alloc] init];

    NSLog(@"before runtime:%@", [father description]);

     

    unsigned int count = 0;

    Ivar *members = class_copyIvarList([Father class], &count);

    for (int i = 0 ; i < count; i++) {

        Ivar var = members[i];

        const char *memberName = ivar_getName(var);

        const char *memberType = ivar_getTypeEncoding(var);

        NSLog(@"%s----%s", memberName, memberType);

    }

}

显示如下:

1

2

3

2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] before runtime:name:wengzilin, age:27

2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] _name----@"NSString"

2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] _age----i

从log中我们知道了,Father类有两个变量,一个公开的包装成属性的age, 类型是int,一个花括号{}内的私有变量_name,类型是NSString。代码中标红色的部分就是runtime.h的api,

class_copyIvarList:获取类的所有属性变量,count记录变量的数量IVar是runtime声明的一个宏,是实例变量的意思,instance variable,在runtime中定义为 typedef struct objc_ivar *Ivari

var_getName:将IVar变量转化为字符串

ivar_getTypeEncoding:获取IVar的类型

如果我们现在想对_name动手,不经过Father同意偷偷修改它呢?我们继续往下做:(接着上面的代码)

1

2

3

Ivar m_name = members[0];

object_setIvar(father, m_name, @"zhanfen");

NSLog(@"after runtime:%@", [father description]);

显示如下:

1

2015-03-17 16:10:28.004 WZLCodeLibrary[38574:3149577] after runtime:name:zhanfen, age:27

我们发现,_name属性被强制改过来了,有wengzilin改为现在zhanfen。

(2)控制私有函数

对于私有变量,我们能做的顶多修改变量的值,但对于私有函数,我们可以玩非常多的花样,比如:在运行时动态添加新的函数、修改私有函数、交换其中两个私有函数的实现、替换私有函数...

同样地,控制的第一步是获得Father类的所有私有方法,我们可以得到.m文件中所有有显式实现的方法以及属性变量的setter+getter方法都会被找到:

1

2

3

4

5

6

7

8

9

10

- (void)tryMemberFunc

{

    unsigned int count = 0;

    Method *memberFuncs = class_copyMethodList([Father class], &count);//所有在.m文件显式实现的方法都会被找到

    for (int i = 0; i < count; i++) {

        SEL name = method_getName(memberFuncs[i]);

        NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];

        NSLog(@"member method:%@", methodName);

    }

}

显示如下:

1

2

3

4

5

6

7

2015-03-17 17:02:33.343 WZLCodeLibrary[38748:3170794] member method:setAge:

2015-03-17 17:02:33.343 WZLCodeLibrary[38748:3170794] member method:age

2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:sayHello

2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:sayGoodbay

2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:description

2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:dealloc

2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:init<br><br>

Method:runtime声明的一个宏,表示一个方法,typedef struct objc_method *Method;

class_copyMethodList:获取所有方法

method_getName:读取一个Method类型的变量,输出我们在上层中很熟悉的SEL

=========

接下来我们试着添加新的方法试试(这种方法等价于对Father类添加Category对方法进行扩展):

1

2

3

4

5

6

7

8

9

10

11

- (void)tryAddingFunction

{

    class_addMethod([Father class], @selector(method::), (IMP)myAddingFunction, "i@:i@");

     

}

//具体的实现,即IMP所指向的方法

int myAddingFunction(id selfSEL _cmd, int var1, NSString *str)

{

    NSLog(@"I am added funciton");

    return 10;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

- (void)tryMemberFunc

{

    //动态添加方法

    [self tryAddingFunction];

    count = 0;

    memberFuncs = class_copyMethodList([Father class], &count);//所有在.m文件显式实现的方法都会被找到

    for (int i = 0; i < count; i++) {

        SEL name = method_getName(memberFuncs[i]);

        NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];

        NSLog(@"member method:%@", methodName);

    }

    //尝试调用新增的方法

    Father *father = [[Father alloc] init];

    [father method:10 :@"111"];//当你敲入father实例后,是无法获得method的提示的,只能靠手敲。而且编译器会给出"-method" not found的警告,可以忽略

    [father release];

}

输入结果:

1

2

3

4

5

6

7

8

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:method::

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:setAge:

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:age

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:sayHello

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:sayGoodbay

2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:description

2015-03-17 17:02:33.346 WZLCodeLibrary[38748:3170794] member method:dealloc

2015-03-17 17:02:33.346 WZLCodeLibrary[38748:3170794] member method:init

我们可以看到,method::方法的确被添加进类中了。有童鞋会问,如果在其他类文件中实例化Father类,还能调用到-method方法吗?答案是可以的,我试验过,尽管无法获得代码提示,但请坚定不移地敲入[father method:xx :xx]方法!

接下来,我们拿系统函数玩玩,目标是让NSString函数的大小写转换功能对调,让APPLE乱套:

1

2

3

4

5

6

7

8

- (void)tryMethodExchange

{

    Method method1 = class_getInstanceMethod([NSString class], @selector(lowercaseString));

    Method method2 = class_getInstanceMethod([NSString class], @selector(uppercaseString));

    method_exchangeImplementations(method1, method2);

    NSLog(@"lowcase of WENG zilin:%@", [@"WENG zilin" lowercaseString]);

    NSLog(@"uppercase of WENG zilin:%@", [@"WENG zilin" uppercaseString]);

}

输出结果:

1

2

2015-03-17 17:20:16.073 WZLCodeLibrary[38861:3180978] lowcase of WENG zilin:WENG ZILIN

2015-03-17 17:20:16.290 WZLCodeLibrary[38861:3180978] uppercase of WENG zilin:weng zilin

本文转载自:http://blog.csdn.net/xin__dong/article/details/46409069

共有 人打赏支持
hanbing94
粉丝 2
博文 66
码字总数 38602
作品 0
朝阳
iOS开发:学习Runtime

学习iOS开发,runtime这个知识点是绕不过去的,但对于我这种学习OC不是太久,写OC的量不够多的人来说,抽象理解runtime的概念或者是看源代码有点枯燥,效果也不好,以例子的方法学习可能会更...

Andy_Ron
06/01
0
0
苹果私有API检查工具开源项目

iOS-private-api-checker 苹果iOS私有API检查工具 Developer tool to scan iOS apps for private API usage before submitting to Apple https://github.com/hustcc/iOS-private-api-checker......

小薇
2015/11/05
0
0
iOS 面试全方位剖析 -- OC语言特性篇 (一)

分类 & 关联对象 & 扩展 & 代理 (一) 通知 KVO KVC 属性关键字. (二) 分类 你用分类都做了哪些事 ? 特点 讲特点是为了能更好的和扩展区分开来 分类中都可以添加哪些内容 分类的实现机制 同一...

PetitBread
05/14
0
0
iOS逆向工程- 学习整理(工具详解)

前言 一、逆向工程的要求 具备丰富的 iOS 开发经验 最好能非常熟悉 iOS 设备的硬件构成,iOS 系统的运行原理。 拿到任意一个 App 之后能够大致推断出它的项目规模和使用的技术,比如它的MVC模...

_小迷糊
05/11
0
0
iOS高仿微信、仪表盘、图片标注图片滤镜、高斯模糊、上拉加载、下拉刷新等源码

iOS精选源码 Swift-图片画框标注(http://www.code4app.com/thread-29884-1-1.html) Swift版的上拉加载, 下拉刷新控件(一句话集成, 超级易用)(http://www.code4app.com/thread-29885-1-1.html...

sunnyaigd
05/22
0
0
iOS时间那点事--NSTimeZone

iOS时间那点事 NSTimeZone **时区是一个地理名字,是为了克服各个地区或国家之间在使用时间上的混乱。 基本概念: GMT 0:00 格林威治标准时间; UTC +00:00 校准的全球时间; CCD +08:00 中国标...

JustBen
2013/08/08
0
0
iOS精美过度动画、视频会议、朋友圈、联系人检索、自定义聊天界面等源码

iOS精选源码 iOS 精美过度动画源码(http://www.code4app.com/thread-14827-1-1.html) iOS简易聊天页面以及容联云IM自定义聊天页面的实现思路(http://www.code4app.com/thread-30348-1-1.htm...

sunnyaigd
07/11
0
0
iOS开发,#define的使用(系列一)

1、判断当前设备是不是iOS7以上版本 #define IOSVERSION7ORABOVE (([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)? (YES):(NO)) 如果判断系统的设备在项目中使用频繁,那么...

召唤攻城狮
2014/04/10
0
0
iOS 面试全方位剖析 -- OC语言特性篇

通过这篇文章要了解的面试问题 请简述分类实现原理 KVO的实现原理是怎样的 能否为分类添加成员变量 目录 分类 & 关联对象 & 扩展 & 代理 通知 KVO KVC 属性关键字 分类 你用分类都做了哪些事...

PetitBread
05/15
0
0
(转)直接拿来用!最火的iOS开源项目(二)

“每一次的改变总意味着新的开始。”这句话用在iOS上可谓是再合适不过的了。GitHub上的iOS开源项目数不胜数,iOS每一次的改变,总会引发iOS开源项目的演变,从iOS 1.x到如今的iOS 7,有的项目...

孙启超
2013/06/21
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SmartyGit安装及拉取rf代码

SmartyGit安装及拉取rf代码 一:生成ssh keys 下载安装包:Git Bash(Git-1.8.3-preview20130601.exe) 安装完成后打开Git Bash,安装流程如下: 在如下图位置找到Git Bash,运行Git Bash在其命...

舒文joven
8分钟前
0
0
Duang,HUAWEI DevEco IDE全面升级啦

想感受全新UI带来的视觉及交互体验、 HiKey970开发板调测、 HiAI API推荐和收藏、 深度AI模型分析等新功能, 体验高清晰度和流畅度的远程AI真机调测吗? 全新的UI设计 采用最优秀的视觉及交互...

华为终端开放实验室
10分钟前
0
0
阻止事件冒泡,阻止默认事件

1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开, 2.event....

闫亚亚
12分钟前
0
0
网络监控工具类

package com.guorentong.learn.organ.utils;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.net.ConnectivityManage......

王先森oO
12分钟前
0
0
命令

sudo nginx -s reload 重启nginx sudo lsof -i -P | grep -i "listen" 查看端口占用

那个猴子
16分钟前
0
0
用scrapy-redis爬去新浪-以及把数据存储到

需求:爬取新浪网导航页(http://news.sina.com.cn/guide/)所有下所有大类、小类、小类里的子链接,以及子链接页面的新闻内容。 准备工作: a.安装redis(windows或者linux) b.安装Redis Des...

丁典
16分钟前
0
0
PHP常用函数篇

1.为什么要使用函数? 除了内建的PHP函数,我们可以创建我们自己的函数。 函数是可以在程序中重复使用的语句块。 使代码逻辑更清晰 避免过多的全局变量 封装后避免相同逻辑重复代码,只需调用...

天地有涯风有信_大海无量不见人
17分钟前
0
0
对List分组

在日常工作中会遇到这样的情景,我们需要对List按照List中对象的一个值进行分组。比如一个Human的List,我们要根据性别分组,传统的方法是做双层循环,逐个对比,今天我要介绍一种详单简单的...

珂jack
19分钟前
0
0
分析jquery ajax jsonpCallback回调函数名包含点号报错问题

现象 项目中涉及到跨域请求,采用jquery ajax jsonp来实现,但是遇到一个奇怪问题,在设置回调函数名称时,若包含点号,如“Callback.Success”,那么执行完成后,其error回调函数始终会被触...

iwaller
23分钟前
0
0
【Graphql实践】使用 Apollo(iOS) 访问 Github 的 Graphql API

最近在协助调研 Apollo 生成的代码是否有可能跨 Query 共享模型的问题,虽然初步结论是不能,并不是预期的结果,但是在调研过程中积累的一些经验,有必要记录下。如果你也对 Graphql 感兴趣,...

ios122
23分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部