运行时Runtime 小结
2015-08-08 22:29
411 查看
第一部分
1. [b]objc_msgSend [/b]这是Objective-C的方法调用的核心,它可调用一个类的所有方法,不管它有没有暴露出来。
例如:
TestObj.h 文件:
TestObj.m 文件:
以下是调用示例:
第二部分
无论目标类有没有将方法暴露出来,一样可以同过运行时将它遍历出来,并可以找出方法的入参.
示例:
1. 方法的入参默认会有两个,这是系统运行时添加进去的。
2. 运行时的入参和返回的数据类型跟OC 的数据类型不同,以下是苹果官方提供的类型对应列表
Table 6-1 Objective-C type encodings
Table 6-2 Objective-C method encodings
第三部分
通过运行时,可以获取方法的变量,并且可以通过映射,动态修改变量名称,MJExtension 框架就是利用了这一特点,所以它可以通过MJExtensionConfig文件来修改参数的映射。
参考:http://www.cnblogs.com/ludashi/p/4673935.html
示例:
第四部分
关联对象(Associated Object),一个对象可以关联任何一个OC对象,包括 block。关联对象有点类似于成员变量,不过它是在运行时添加进去的。关联对象使用的是键值对的形式进行关联,同时还需要指定相应的内存管理策略。
涉及到的方法如下:
objc_setAssociatedObject(idobject,
constvoid *key,id value,
objc_AssociationPolicypolicy) // 设置绑定对象
objc_getAssociatedObject(idobject,
constvoid *key) // 获得绑定对象
objc_removeAssociatedObjects(idobject) // 移除绑定对象
绑定对象的时候,需要根据对象的属性,设置不同的关联策略,也就是Objc的内存管理的引用计数机制,包括有:
OBJC_ASSOCIATION_ASSIGN =0,
/**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC =1,/**<
Specifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY_NONATOMIC =3,
/**< Specifies that the associated object is copied.
OBJC_ASSOCIATION_RETAIN =01401,
/**< Specifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY =01403 /**<
Specifies that the associated object is copied.
示例代码:
第五部分
Method Swizzling,本身Objc的方法调用是通过消息转发机制来实现的,既然如此,就可以通过重新映射方法对应的实现来达到“偷天换日”的目的。跟消息转发相比,Method Swizzling 的做法更为隐蔽,甚至有些冒险,也增大了debug的难度。
代码示例:
Demo: https://github.com/AbooJan/RunTimeDemo.git
1. [b]objc_msgSend [/b]这是Objective-C的方法调用的核心,它可调用一个类的所有方法,不管它有没有暴露出来。
例如:
TestObj.h 文件:
@interface TestObj : NSObject @property (nonatomic, assign) NSInteger userId; @property (nonatomic, copy) NSString *userName; @property (nonatomic, assign) BOOL gender; + (NSInteger)test; + (NSString *)testWithArg:(NSInteger)arg1 arg2:(NSString *)arg2; - (BOOL)isChnage; @end
TestObj.m 文件:
@implementation TestObj + (NSInteger)test { NSLog(@"这是测试方法"); return 6; } + (NSString *)testWithArg:(NSInteger)arg1 arg2:(NSString *)arg2 { NSLog(@"测试方法 : %ld -- %@", arg1, arg2); return @"test"; } + (void)test2 { NSLog(@"内部方法"); } - (BOOL)isChnage { NSLog(@“有没有改变"); return NO; } - (NSString *)changeUserName:(NSString *)userName { return @"修改后的名字"; } @end
以下是调用示例:
// 直接调用类方法,返回值需要强转,也可以不处理返回值 NSInteger test1 = (NSInteger)objc_msgSend(objc_getClass("TestObj"), sel_registerName("test")); NSLog(@"测试1:%ld", test1); // 调用有参数的类方法 NSString *test2 = (NSString *) objc_msgSend(objc_getClass("TestObj"), @selector(testWithArg:arg2:), 6, @"消息"); NSLog(@"测试2:%@", test2); // 直接调用TestObj里面的类方法,尽管它没有暴露出来, 这就是运行时的厉害之处 objc_msgSend(objc_getClass("TestObj"), sel_registerName("test2")); // 调用方法 TestObj *testObj = [TestObj new]; objc_msgSend(testObj, sel_registerName("isChnage"));
第二部分
无论目标类有没有将方法暴露出来,一样可以同过运行时将它遍历出来,并可以找出方法的入参.
示例:
unsigned int methodCount; Method *methods = class_copyMethodList(objc_getClass("TestObj"), &methodCount); for (int i = 0; i < methodCount; i++) { Method tempMethod = methods[i]; NSLog(@“方法名称:%s", sel_getName(method_getName(tempMethod))); NSLog(@“入参个数:%d", method_getNumberOfArguments(tempMethod)); NSLog(@“%s”, method_getTypeEncoding(tempMethod)); NSLog(@“返回值类型:%s”, method_copyReturnType(tempMethod)); IMP imp = method_getImplementation(tempMethod); NSLog(@"IMP: %@", imp_getBlock(imp)); NSLog(@"---入参----\n"); unsigned int argCount = method_getNumberOfArguments(tempMethod); for (int j = 0; j < argCount; j++) { NSLog(@"%s", method_copyArgumentType(tempMethod, j)); } } // 手动释放,避免内存泄露 free(methods);
1. 方法的入参默认会有两个,这是系统运行时添加进去的。
2. 运行时的入参和返回的数据类型跟OC 的数据类型不同,以下是苹果官方提供的类型对应列表
Table 6-1 Objective-C type encodings
Code | Meaning |
---|---|
c | A char |
i | An int |
s | A short |
l | A long lis treated as a 32-bit quantity on 64-bit programs. |
q | A long long |
C | An unsigned char |
I | An unsigned int |
S | An unsigned short |
L | An unsigned long |
Q | An unsigned long long |
f | A float |
d | A double |
B | A C++ boolor a C99 _Bool |
v | A void |
* | A character string (char *) |
@ | An object (whether statically typed or typed id) |
# | A class object (Class) |
: | A method selector (SEL) |
[array type] | An array |
{name=type...} | A structure |
(name=type...) | A union |
bnum | A bit field of num bits |
^type | A pointer to type |
? | An unknown type (among other things, this code is used for function pointers) |
Code | Meaning |
---|---|
r | const |
n | in |
N | inout |
o | out |
O | bycopy |
R | byref |
V | oneway |
通过运行时,可以获取方法的变量,并且可以通过映射,动态修改变量名称,MJExtension 框架就是利用了这一特点,所以它可以通过MJExtensionConfig文件来修改参数的映射。
参考:http://www.cnblogs.com/ludashi/p/4673935.html
示例:
unsigned int propertyCount; objc_property_t *propertys = class_copyPropertyList(objc_getClass("TestObj"), &propertyCount); for (int i = 0; i < propertyCount; i++) { objc_property_t tempProperty = propertys[i]; NSLog(@“成员变量名称:%s", property_getAttributes(tempProperty)); NSLog(@"成员变量描述:%s”, property_getAttributes(tempProperty)); } // 释放 free(propertys);
第四部分
关联对象(Associated Object),一个对象可以关联任何一个OC对象,包括 block。关联对象有点类似于成员变量,不过它是在运行时添加进去的。关联对象使用的是键值对的形式进行关联,同时还需要指定相应的内存管理策略。
涉及到的方法如下:
objc_setAssociatedObject(idobject,
constvoid *key,id value,
objc_AssociationPolicypolicy) // 设置绑定对象
objc_getAssociatedObject(idobject,
constvoid *key) // 获得绑定对象
objc_removeAssociatedObjects(idobject) // 移除绑定对象
绑定对象的时候,需要根据对象的属性,设置不同的关联策略,也就是Objc的内存管理的引用计数机制,包括有:
OBJC_ASSOCIATION_ASSIGN =0,
/**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC =1,/**<
Specifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY_NONATOMIC =3,
/**< Specifies that the associated object is copied.
OBJC_ASSOCIATION_RETAIN =01401,
/**< Specifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY =01403 /**<
Specifies that the associated object is copied.
示例代码:
// 绑定对象 TestObj2 *a = [TestObj2 new]; a.userID = 1001; a.userName = @"AbooJan"; a.books = @[@"Harry", @"永无止境", @"编程思想"]; _test = [[TestObj alloc] init]; objc_setAssociatedObject(_test, "testObj", a, OBJC_ASSOCIATION_RETAIN); objc_setAssociatedObject(_test, "testNum", @(666), OBJC_ASSOCIATION_ASSIGN); // 获得绑定对象 TestObj2 *a = objc_getAssociatedObject(_test, "testObj”); NSNumber *testNum = objc_getAssociatedObject(_test, "testNum"); NSLog(@"%@", [a description]); NSLog(@"%ld", [testNum integerValue]);
第五部分
Method Swizzling,本身Objc的方法调用是通过消息转发机制来实现的,既然如此,就可以通过重新映射方法对应的实现来达到“偷天换日”的目的。跟消息转发相比,Method Swizzling 的做法更为隐蔽,甚至有些冒险,也增大了debug的难度。
代码示例:
/* * 1. 如果是一个子类,一般在 initialize 方法中替换方法 * 2. 如果是一个分类,一般在 load 方法中实行替换 * * 在以上两个方法中替换,是保证它在一启动的时候就实行替换 * * * load和initialize有很多共同特点,下面简单列一下: * 1.在不考虑开发者主动使用的情况下,系统最多会调用一次 * 2.如果父类和子类都被调用,父类的调用一定在子类之前 * 3.都是为了应用运行提前创建合适的运行环境 * 4.在使用时都不要过重地依赖于这两个方法,除非真正必要 * * * +load 能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。 * +initialize 在其所属类的方法被调用或类初始化时会被调用,否则它可能永远不会被调用 * */ + (void)load { NSLog(@"load"); } + (void)initialize { // dispatch_once是GCD中的一个方法,它保证了代码块只执行一次,并让其为一个原子操作, // 保证线程的安 全,避免并发引发问题,认为它是Method Swizzling 的最佳实现 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 第一种初始化方法 Class target = [self class]; Method originalMethod = class_getInstanceMethod(target, sel_registerName("description")); Method swizzMethod = class_getInstanceMethod(target, sel_registerName("swizzle_description")); // 第二种初始化方法 // Class aClass = object_getClass((id)self); // // Method originalMethod = class_getClassMethod(aClass, @selector(description)); // Method swizzledMethod = class_getClassMethod(aClass, @selector(swizzle_description)); /* * object_getClass((id)self) 与 [self class] 返回的结果类型都是 Class, * 但前者为元类,后者为其本身,因为此时 self 为 Class 而不是实例。 */ /* * 如果类中不存在要替换的方法,那就先用class_addMethod和class_replaceMethod函数添加 * 和替换两个方法的实现;如果类中已经有了想要替换的方法,那么就直接调 * 用 method_exchangeImplementations函数交换了两个方法的 IMP。 * 个人建议将要替换的方法在当前类先实现出来. */ // BOOL didAddMethod = class_addMethod(target, method_getName(originalMethod), method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod)); // // if (didAddMethod) { // // class_replaceMethod(target, method_getName(swizzMethod), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); // // }else{ // method_exchangeImplementations(originalMethod, swizzMethod); // } // 如果确定已添加替换的方法,直接执行替换就可以了 method_exchangeImplementations(originalMethod, swizzMethod); }); } - (NSString *)description { NSLog(@"原来的方法"); return [super description]; } - (void)swizzle_description { NSLog(@"这是替换的方法"); // 本身替换的方法只会执行一次,当执行以下方法的时候,它就会调用该类原来的方法了 [self aboo_description]; // 如果调用以下方法,就会进入死循环 // [self description]; }
Demo: https://github.com/AbooJan/RunTimeDemo.git
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
- Android、iOS和Windows Phone中的推送技术详解
- java显示当前运行时的参数(java运行参数)
- IOS 改变键盘颜色代码
- Android和IOS的浏览器中检测是否安装某个客户端的方法
- javascript实现阻止iOS APP中的链接打开Safari浏览器
- iOS开发之路--微博OAuth授权_取得用户授权的accessToken
- ios通过按钮点击异步加载图片
- 仅需几行代码实现方便易用的状态栏指示器