runtime心得
来源:互联网 发布:淘宝花呗分期扣全款 编辑:程序博客网 时间:2024/05/19 03:17
近日看了一些 runtime 的资料,结合之前对 runtime 的认识,理解又深了一些。runtime 的使用网上资料很多,不再赘述,此处只分享一些我学习和使用过程中遇到的点。
一:成员变量包括实例变量和类变量,因为 iOS中无真正的类变量,所以,成员变量都是(就是)实例变量。
首先我想大家先搞懂一些基础知识,因为看 runtime 博客的时候会经常看到实例变量和成员变量,如果不清楚两者的关系,有可能会搞混淆,对于相关知识的理解就会很吃力。
java 里面默认情况下,成员变量是实例成员,在外部需要通过对象才能操作。如果用static修饰,就成为了静态成员,也称为类变量,无需通过对象就可以操作。(成员变量包括实例变量和类变量)
对于Object-C ,我查了相关资料说OC不支持真正的类变量。所以应该可以理解为 iOS 中成员变量都是实例变量。
好,接下来咱们聊 runtime。
二:一旦完成类定义,就不能再添加成员变量了
class_addProperty方法添加的属性,系统不会自动添加实例变量,即不会在class_copyIvarList内获取到该属性的成员变量。有别于在类定义时(category 里@property 修饰,系统也不会自动添加实例变量)直接用@property 修饰
在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是文档中特别说明:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
意思是说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair之后才可以被使用,同样没有机会再添加成员变量。
以下是代码验证:
.h文件
@interface SomeClass : NSObject { NSString *age; NSInteger weight;}@property (nonatomic, copy) NSString *aaa;@end
.m 文件
@implementation SomeClass-(id)init { self = [super init]; //添加属性 [self addPropertyWithPropertyName:@"ccc" withValue:@"1234"]; unsigned int count1 = 0; //获取属性列表 objc_property_t *pros = class_copyPropertyList([SomeClass class], &count1); for (int i = 0; i < count1; i++) { objc_property_t pro = pros[i]; NSString *proName = [NSString stringWithCString:property_getName(pro) encoding:NSUTF8StringEncoding]; //***打印1*** NSLog(@"proName:%@\n",proName); } free(pros); unsigned int count = 0; //获取成员变量列表 Ivar *members = class_copyIvarList([SomeClass class], &count); for (int i = 0; i < count; i++) { Ivar ivar = members[i]; NSString *ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; //***打印2*** NSLog(@"ivarName:%@\n",ivarName); } free(members); return self;}-(void)addPropertyWithPropertyName:(NSString *)propertyName withValue:(id)value{ objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])] UTF8String] }; objc_property_attribute_t ownership = { "&", "N" }; objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] }; objc_property_attribute_t attrs[] = { type, ownership, backingivar }; if (class_addProperty([self class], [propertyName UTF8String], attrs, 3)) { //赋值 [self setValue:value forKey:propertyName]; NSLog(@"%@", [self valueForKey:propertyName]); NSLog(@"创建属性Property成功"); }}
打印1:
2017-02-15 18:21:43.934774 SomeThingTest[2715:953599] proName:ccc
2017-02-15 18:21:43.934788 SomeThingTest[2715:953599] proName:aaa
此处打印的是.h 里的aaa,运行时添加的属性ccc
打印2:
2017-02-15 18:21:43.934876 SomeThingTest[2715:953599] ivarName:age
2017-02-15 18:21:43.934897 SomeThingTest[2715:953599] ivarName:weight
2017-02-15 18:21:43.934910 SomeThingTest[2715:953599] ivarName:_aaa
此处打印的是.h 里的实例变量 age,weight 以及@property 修饰的 aaa,系统自动生成的实例变量_aaa.
三:Method Swizzling 实现友盟页面统计。我把下载链接放到下面了
应该大多数应用都有页面统计的要求。大多实现,1.最冗余的做法就是每个 VC都写上相关代码。2.好点的写个 Base 类,在该类中写上统计的相关代码,开发中让其他 VC 继承自改 Base类。
做法2肯定比做法1简洁了很多,但其实并不好维护, 比如项目进来个新人,或者交接时必须告知new 新的 ViewController时继承该 Base 类,而且有些同事要写一个 tableView 的界面,可能他直接创建了一个 继承自UITableViewController的 VC…
所以这个时候就该 Method Swizzling出场了,当然也许还有更好的方法。此处用 swizzling的原理就是用一个新的方法的实现和系统方法的实现作交换,这个新方法的实现包括系统方法的实现,同时还注入了一些自定义的行为,自定义行为就是我们想要做的操作。
首先 new 一个 category,UIViewController 的分类,此处我命名为UIViewController+Swizzling。
+ (void)load 方法是在运行期提前并且自动调用的方法。我们可以利用他们在类被使用前,做一些预处理工作。父类,子类,分类的 load 方法依次调用,在子类或者分类重写 load 方法不用调用[super load]; 在category 里除了+ (void)load 方法,重写类的同名方法会覆盖该类这个方法。使用dispatch_once保证 method swizzling 只发生一次,避免手动调用+ (void)load交换方法实现后还原到最初状态。
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self exchangeViewWillAppear ]; [self exchangeViewWillDisappear]; });}//交换 viewWillAppear+ (void)exchangeViewWillAppear{ // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。 Method fromMethod = class_getInstanceMethod([self class], @selector(viewWillAppear:)); Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewWillAppear)); /** * 我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。 * 而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。 * 所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。 */ if (!class_addMethod([self class], @selector(swizzlingViewWillAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) { method_exchangeImplementations(fromMethod, toMethod); }}//交换 viewWillDisappear+ (void)exchangeViewWillDisappear{ Method fromMethod = class_getInstanceMethod([self class], @selector(viewWillDisappear:)); Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewWillDisappear)); if (!class_addMethod([self class], @selector(swizzlingViewWillAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) { method_exchangeImplementations(fromMethod, toMethod); }}/* 我们自己实现的用来替换viewWillAppear的方法 */- (void)swizzlingViewWillAppear { NSString *class = NSStringFromClass(self.class);//[NSString stringWithFormat:@"%@", self.class]; // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉 if(![class containsString:@"UI"]){ NSLog(@"统计打点-beginLogPageView : %@", self.class); [MobClick beginLogPageView:class]; } //此处调的方法内部实现已替换成 viewWillAppear 方法 [self swizzlingViewWillAppear];}/* 我们自己实现的用来替换viewWillDisappear的方法 */- (void)swizzlingViewWillDisappear { NSString *class = NSStringFromClass(self.class);//[NSString stringWithFormat:@"%@", self.class]; // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉 if(![class containsString:@"UI"]){ NSLog(@"统计打点-endLogPageView : %@", self.class); [MobClick endLogPageView:class]; } //此处调的方法内部实现已替换成 viewWillDisappear 方法 [self swizzlingViewWillDisappear];}
UIViewController+Swizzling下载
- runtime心得
- 使用Runtime.exec()心得
- runtime的理解和心得
- 关于runtime的使用心得
- iOS:学习runtime的理解和心得
- iOS runtime的一些心得实践
- iOS:学习runtime的理解和心得
- iOS:学习runtime的理解和心得
- 对J2SE中Runtime类得一些实践心得
- iOS开发——学习runtime的理解和心得
- runtime
- Runtime
- Runtime
- Runtime
- runtime
- runtime
- Runtime
- runtime*****
- 数据库
- if __name__ == '__main__' 如何正确理解?
- 测试系统是大段还是小端存储的c代码
- Effective C++ 条款08:别让异常逃离析构函数
- Javascript中document.execCommand()的用法
- runtime心得
- 打印十字图 解题报告
- PHP-字符串操作
- ServletContext,ActionContext,ServletActionContext 的区别
- Android Canvas绘图详解【转】
- [bzoj1015][JSOI2008]星球大战starwar
- python数据分析scipy简单例子
- Android onTouch()和onTouchEvent()区别
- MySQL数据库行转换为列的2种方法