如何建网站详细步骤,wordpress主题 kratos,做问卷不花钱的网站,湖北田源建设工程有限公司网站0、简介#xff1a; OC方法不同于C语言函数#xff0c;属于动态调用过程#xff0c;在编译的时候并不能决定真正调用哪个函数#xff0c;只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。 至于其他理论上的东西不必讲太多#xff0c;编程讲的就是实用性 OC方法不同于C语言函数属于动态调用过程在编译的时候并不能决定真正调用哪个函数只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。 至于其他理论上的东西不必讲太多编程讲的就是实用性只记录一下各种使用场景。 使用运行时:1导入objc/message.h 2Build Setting - 搜索msg - 设置属性为No取消消息机制的检查 一般见人写runtime第一个必讲消息机制发消息是怎么回事 比如新建一个dog类,头文件定义三个外部调用方法内部实现这里就不写了随意 1 - (void)run; //对象方法
2
3 (void)run; //类方法
4
5 - (void)eat:(NSString *)food; //带参数的实例方法 来到使用它的地方 1 Dog *d [[Dog alloc] init];2 3 [d run];4 5 // 消息机制:任何方法调用,本质都是发送消息6 // SEL:方法编号,根据方法编号就可以找到对应方法实现7 [d performSelector:selector(run)];8 9 // 让d发送消息
10 objc_msgSend(d, selector(run)); 基本可以理解为第3行代码底层调用第7行第7行底层调用第10行 顺便也写一下OC方法的大概调用流程吧 简单理解下就行了看了下网上大神写的资料感觉没必要记那么多繁复的东西 大概我理解就是方法调用就是个发消息的过程消息名即方法名接收消息的对象即我们普遍认为的调用那个方法的对象。 SEL即方法编号第3行调用方法之后去接收者那里找到对应的方法编号通过方法编号找到方法映射表中的对应方法最后根据方法映射表找到对应的方法实现。 来个图更明显点 为避免误读对上面的图做个补充以免为初学者产生一个错误的映射表内存模型 类与对象相比只是多了实例变量和方法列表等类和对象都是对象分别是类对象和实例对象。 在class中的isa指针指向的是metaClassmetaClass中存放的是静态成员变量和类方法开头。在object中的isa指针指向的是对应的类结构ClassClass其中存放的是普通成员变量和实例方法-开头。 所有的metaclass中isa指针都是指向根metaclass而根metaclass则指向自身。根metaclass是通过继承根类产生的与根class结构体成员一致不同的是根metaclass的isa指针指向自身。 实例对象存放对象方法的映射表类对象存放类方法的映射表因此上面的两个方法其实是在不同的映射表中的。 上面的代码是对象无参调用的方式补充一下类方法和含参方法 1 2 //类名调用类方法的本质就是类名转换成类对象3 [Dog run];4 5 // 获取类对象6 Class dogClass [Dog class];7 8 //上面方法会调用这个9 [dogClass performSelector:selector(run)];
10
11 //最终干的事还是发消息
12 objc_msgSend(dogClass, selector(eat)); 最后说含参方法 1 //含参方法
2 [d eat:789];
3
4 [d performSelector:selector(eat:) withObject:456];
5
6 objc_msgSend(d, selector(eat:),123); 顺便补充一下含参方法performSelector最多只能传入两个参数参数更多的时候多余的参数可以放到字典、数组里这个不存在问题demo里面有写。 1、当你希望给系统方法扩展一些功能并且保持原有的功能时 直接上代码 分类 #import UIKit/UIKit.hinterface UIColor (extension)(__kindof UIColor *)at_redColor;
end#import UIColorextension.h
#import objc/message.himplementation UIColor (extension)// 加载这个分类的时候就会调用load方法(void)load
{ // class:获取这个类// SEL:获取方法编号,根据SEL就能去对应的类找方法Method redColorMethod class_getClassMethod([UIColor class], selector(redColor));// 获取类方法Method at_redColorMethod class_getClassMethod([UIColor class], selector(at_redColor));// 交换方法实现method_exchangeImplementations(redColorMethod, at_redColorMethod);}//自己的方法(__kindof UIColor *)at_redColor {UIColor *color [UIColor at_redColor];//添加一个打印功能NSLog(123);return color;
}
end 控制器 1 self.view.backgroundColor [UIColor redColor]; 会打印出123。 红线表示方法交换 2、动态给某个类添加方法如果一个类方法非常多加载类到内存的时候也比较耗费资源(感觉也耗不了什么资源非要说的话跟懒加载的思想差不多吧工作中基本没这么玩过)需要给每个方法生成映射表 1 #import Dog.h2 #import objc/message.h3 4 implementation Dog5 // 定义函数6 // 默认一个方法都有两个隐式参数, self:方法调用者, _cmd:调用方法的编号7 void runImp(id self, SEL _cmd, NSString *param)8 {9 NSLog(调用run % % %,self,NSStringFromSelector(_cmd),param);
10 }
11
12 // 动态添加方法,首先实现这个resolveInstanceMethod
13 // resolveInstanceMethod调用:当调用了没有实现的方法没有实现就会调用resolveInstanceMethod
14 // resolveInstanceMethod作用:知道哪些方法没有实现,从而动态添加方法
15 // sel:没有实现方法
16 (BOOL)resolveInstanceMethod:(SEL)sel
17 {
18 // NSLog(%,NSStringFromSelector(sel));
19 // 动态添加run方法
20
21 if (sel selector(run:)) {
22 /*
23 cls:给哪个类添加方法
24 SEL:添加方法的方法编号是什么
25 IMP:方法实现,函数入口,函数名
26 types:方法类型
27 */
28 // :对象 :SEL
29 class_addMethod(self, sel, (IMP)runImp1, v:);
30
31 // 处理完
32 return YES;
33
34 }
35 return [super resolveInstanceMethod:sel];
36 }
37 end 使用 会调用runImp方法 1 Dog *dog [[Dog alloc] init];
2 [dog performSelector:selector(run:) withObject:跑啊]; 说明那两个隐式参数可写可不写types:方法类型只是对runImp方法的类型说明具体说明可以搜官方文档v代表返回值void,代表对象:代表SEL。但是我故意把方法类型改错了也就是和方法定义的真实类型不匹配的时候运行也没什么问题。 3、在分类中添加属性 都知道你在分类中定义属性的时候只会生成get和set方法的声明不会生成实际的成员变量和方法实现。再顺便说一句分类没有父类 第一个想到的办法可能是自己定义一个全局变量像这样 代码: 1 interface NSObject (ATTest)
2 property (nonatomic, copy) NSString *sex;
3 end 1 implementation NSObject (ATTest)
2 NSString *_sex;
3 - (void)setSex:(NSString *)sex {
4 _sex sex;
5 }
6 - (NSString *)sex {
7 return _sex;
8 }
9 end 尝试用一下 1 NSObject *objText [[NSObject alloc] init];
2 objText.sex 女博士;
3 NSLog(%,objText.sex);
4
5 NSObject *objText1 [[NSObject alloc] init];
6 NSLog(%,objText1.sex); 两个对象打印出来的都是“女博士” 因此给一个类声明属性本质就是给这个类 和 属性值 设置关联使类中的属性指向属性值的内存空间而上面的做法是直接把这个值的内存空间添加到了这个类的内存空间。 下面是runtime做法: 1 interface NSObject (ATDog)
2 property (nonatomic, copy) NSString *name;
3 end 1 #import NSObjectATDog.h2 #import objc/message.h3 4 implementation NSObject (ATDog)5 - (void)setName:(NSString *)name {6 //设置关联属性7 /* 参数说明8 object:添加属性的对象9 key属性名
10 value属性关联的值
11 policy:属性策略就是strong,copy那些东西
12 */
13 objc_setAssociatedObject(self, name, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
14 }
15 - (NSString *)name {
16 //获取关联属性
17 /* 参数说明
18 object:获取属性的对象
19 key属性名
20 */
21 return objc_getAssociatedObject(self, name);
22 }
23 end 使用 1 NSObject *obj [[NSObject alloc] init];
2 obj.name 1;
3 NSLog(%,obj.name);
4
5 NSObject *obj1 [[NSObject alloc] init];
6 obj1.name 2;
7 NSLog(%,obj1.name); 前面打印“1”后面打印“2”一个obj对象对应一个属性 4、自动生成模型属性代码的工具类 不用每次手打属性了挺方便的 没有访问网络直接搞了个plist文件反正都一样的plist文件是微博首页的微博列表具体见最下面demo 先复习一下读取plist: 1 //读取plist(最外层一个字典字典里是个大数组)
2 NSString *path [[NSBundle mainBundle] pathForResource:status.plist ofType:nil];
3 NSDictionary *dict [NSDictionary dictionaryWithContentsOfFile:path];
4 NSArray *arr dict[statuses]; 1 //调用分类方法打印模型属性代码
2 [NSObject createPropertyCodeWithDict:arr[2][user]]; 打印user模型属性是这个样子的 现在看一下工具类是怎么实现的吧先声明一个类方法供外部调用之后是实现如果还有没考虑到的类型自行添加就OK具体实现就不说了代码已经很清楚了。也可以自己看一下下面的demo 1 interface NSObject (ATProperty)
2
3 (void)createPropertyCodeWithDict:(NSDictionary *)dict;
4 end 1 implementation NSObject (ATProperty)2 (void)createPropertyCodeWithDict:(NSDictionary *)dict {3 NSMutableString *propertyCode [NSMutableString string];4 5 //遍历字典6 [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {7 // NSLog(% %, key, [obj class]);8 9 NSString *code nil;
10 //判断不同类型的属性定义代码
11 if ([obj isKindOfClass:NSClassFromString(__NSCFString)]) {
12 code [NSString stringWithFormat:property (nonatomic, copy) NSString *%;, key];
13 } else if ([obj isKindOfClass:NSClassFromString(__NSCFBoolean)]) {
14 code [NSString stringWithFormat:property (nonatomic, assign) BOOL %;, key];
15 } else if ([obj isKindOfClass:NSClassFromString(__NSCFNumber)]) {
16 code [NSString stringWithFormat:property (nonatomic, assign) NSUInteger %;, key];
17 } else if ([obj isKindOfClass:NSClassFromString(__NSCFDictionary)]) {
18 code [NSString stringWithFormat:property (nonatomic, copy) NSDictionary *%;, key];
19 } else if ([obj isKindOfClass:NSClassFromString(__NSCFArray)]) {
20 code [NSString stringWithFormat:property (nonatomic, copy) NSArray *%;, key];
21 }
22 //拼接字符串
23 [propertyCode appendFormat:\n%\n, code];
24 }];
25 NSLog(%,propertyCode);
26 }
27 end 5、字典转模型 1、直接用KVC 1、导入plist文件导入上面打印模型属性代码的工具类新建status模型类将打印出来的属性代码copy进去 打印属性代码的时候的一点问题 1 //先打印一下属性代码(有时候每个字典里不一定每个属性都有比如转发微博的属性有的微博有转发有的则没有所以
2 // 用这个打印出的模型属性放到模型里不一定是全部属性都有的还好如果崩溃的话会提醒你哪个没有写上去)
3 [NSObject createPropertyCodeWithDict:arr[0]]; 2、解析plist 1 //解析plist
2 NSString *path [[NSBundle mainBundle] pathForResource:status.plist ofType:nil];
3 NSDictionary *dict [NSDictionary dictionaryWithContentsOfFile:path];
4 NSArray *arr dict[statuses]; 3、转模型 1 //转模型
2 NSMutableArray *statuses [NSMutableArray array];
3 for (NSDictionary *statusDict in arr) {
4 ATStatus *status [ATStatus statusWithDict:statusDict];
5 [statuses addObject:status];
6 }
7 NSLog(%,statuses); 在模型类中实现转模型方法statusWithDict的代码 1 //转模型实现
2 (ATStatus *)statusWithDict:(NSDictionary *)dict {
3 ATStatus *status [[self alloc] init];
4 //KVC
5 [status setValuesForKeysWithDictionary:dict];
6 return status;
7 } 4、还有些问题1服务器返回的字段不一定都用的到但是现有方法如果不把服务器返回的所有字段属性都写到模型的话就会因为找不到key崩溃2服务器经常返回属性名id的字段但是id在OC中是关键字直接写id可能会导致一些问题所以最好在模型中给丫换个名字比如ID这两个问题都能用下面的方法解决 1 //解决KVC对应属性崩溃找不到崩溃2 - (void)setValue:(id)value forUndefinedKey:(NSString *)key {3 //key:没有找到的key4 //value:没有找到的key对应的value5 6 //找不到id,把id的值赋给ID7 if ([key isEqualToString:id]) {8 _ID [value integerValue];9 }
10 //打印出没找到的key
11 NSLog(unFind: key:% value:%, key, value);
12 } 实现效果最下有代码demo 打个断点看到已经成功转模型。 2、runtime转模型 二者的区别 KVC:遍历字典中所有key,去模型中查找有没有对应的属性名没找到就会崩 runtime:遍历模型中所有属性名,去字典中查找如果找不到也不会崩 1、导入plist文件导入上面打印模型属性代码的工具类新建status模型类将打印出来的属性代码copy进去 2、新建一个分类做转模型的工具类具体用法注释已经很详细了包括服务器返回id的问题我把id一律在模型中定义为ID #import objc/message.himplementation NSObject (ATObjectModel)(__kindof NSObject *)objectModelWithDict:(NSDictionary *)dict {//创建对应模型类id obj [[self alloc] init];//成员属性数量unsigned int count 0;//获取模型类属性列表数组Ivar *ivarList class_copyIvarList(self, count);//遍历所有成员属性for (int i 0; i count; i) {//获取成员属性(Ivar)Ivar ivar ivarList[i];//获取成员属性名NSString *propertyName [NSString stringWithUTF8String:ivar_getName(ivar)];//去掉proprtyName前面的下划线propertyName [propertyName substringFromIndex:1];//获取valueid value nil;if ([propertyName isEqualToString:ID]) {value dict[id];} else {value dict[propertyName];}if (value) {//KVC赋值不能传空[obj setValue:value forKey:propertyName];}}//C语言函数ARC不会自动释放需要手动释放free(ivarList);return obj;
}
end 使用就很简单了 1 NSString *path [[NSBundle mainBundle] pathForResource:status.plist ofType:nil];2 NSDictionary *dict [NSDictionary dictionaryWithContentsOfFile:path];3 NSArray *array dict[statuses];4 // [NSObject createPropertyCodeWithDict:array[0]];5 6 NSMutableArray *statuses [NSMutableArray array];7 for (NSDictionary *dict in array) {8 //runtime转模型9 ATStatus *status [ATStatus objectModelWithDict:dict];
10 [statuses addObject:status];
11 }
12 NSLog(%,statuses); 打个断点查看statuses OK。 当然上面只是最外层的转换那么value如果是字典或者数组呢 这就需要在上面的代码里再加点东西 首先是字典的情况 1 //二级转换2 //获取成员属性类型3 NSString *propertyType [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];4 //如果value是字典(实质是字典类型但是不是NSDictionary因为如果类型还是NSDictionary没有必要转换)5 if ([value isKindOfClass:[NSDictionary class]] ![propertyType containsString:NS]) {6 // NSLog(%, propertyType);7 //获取属性类型(剪切字符串\ATUser\)8 NSString *type [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];9 NSRange range [type rangeOfString:\];
10 type [type substringFromIndex:range.location range.length];
11 range [type rangeOfString:\];
12 type [type substringToIndex:range.location];
13
14 Class modelClass NSClassFromString(type);
15 if (modelClass) { //有对应的类型才需要转
16 value [modelClass objectModelWithDict:value];
17 }
18 } 数组的情况 1 //三级转换如果value是数组数组中再包含字典2 if ([value isKindOfClass:[NSArray class]]) {3 //如果模型类实现了字典数组转模型数组的协议4 if ([self respondsToSelector:selector(ModelClassInArray)]) {5 //转换成id类型就能调用任何对象的方法6 id idSelf self;7 //获取数组中的模型8 NSString *type [idSelf ModelClassInArray][propertyName];9 Class modelClass NSClassFromString(type);
10
11 NSMutableArray *dictArr [NSMutableArray array];
12 //遍历数组
13 for (NSDictionary *dict in value) {
14 //转模型
15 id model [modelClass objectModelWithDict:dict];
16 [dictArr addObject:model];
17 }
18 //把模型数组赋值给value
19 value dictArr;
20 }
21 } 数组的情况需要转模型的工具类提供一个协议供ATStatus遵守并实现返回一个字典告诉工具类数组中是一个什么样的字典这样工具类才知道给他转成一个什么类的模型。 最后仍然是KVC赋值。 可以看见数组和字典中都转换成model对象了而且我特意在每层添加了id,经过上面对id的处理也没有问题了id的处理做的比较简单只要在定义模型类的时候把id的情况改成ID就行了其他情况就没有处理比如ie也是个关键字呢没做到人家框架中可以让使用者自定义模型属性叫什么的程度。 注意当你把NSDictionary改成 ATUser 的时候注意策略如果不匹配也要改一下 我一开始就忘改了结果用的是copy然后就崩了要改成strong。 6、利用runtime归档和解档 同样是用NSObject分类来做这个工具类 先看头文件提供的方法 1 interface NSObject (ATArchiver)
2 - (NSArray *)ignorePropertyNames;
3 - (void)encode:(NSCoder *)encoder;
4 - (void)decode:(NSCoder *)decoder;
5 end 具体实现归档和解档比较简单没写注释说一下大体流程1获取调用类的属性列表2遍历属性propertyName [propertyName substringFromIndex:1];这句代码的意思是去掉成员变量名前面的下划线3如果有需要忽略的属性忽略掉4归解档5释放ivarList 1 - (void)encode:(NSCoder *)encoder {2 unsigned int count 0;3 Ivar *ivarList class_copyIvarList([self class], count);4 for (int i 0; i count; i) {5 Ivar ivar ivarList[i];6 NSString *propertyName [NSString stringWithUTF8String:ivar_getName(ivar)];7 propertyName [propertyName substringFromIndex:1];8 9 if ([self respondsToSelector:selector(ignorePropertyNames)]) {
10 if ([[self ignorePropertyNames] containsObject:propertyName]) {
11 continue;
12 }
13 }
14 id value [self valueForKey:propertyName];
15 [encoder encodeObject:value forKey:propertyName];
16 }
17 free(ivarList);
18 }
19 - (void)decode:(NSCoder *)decoder {
20 unsigned int count 0;
21 Ivar *ivarList class_copyIvarList([self class], count);
22 for (int i 0; i count; i) {
23 Ivar ivar ivarList[i];
24 NSString *propertyName [NSString stringWithUTF8String:ivar_getName(ivar)];
25 propertyName [propertyName substringFromIndex:1];
26 if ([self respondsToSelector:selector(ignorePropertyNames)]) {
27 if ([[self ignorePropertyNames] containsObject:propertyName]) {
28 continue;
29 }
30 }
31 id value [decoder decodeObjectForKey:propertyName];
32 [self setValue:value forKey:propertyName];
33 }
34 free(ivarList);
35 } 控制器里面使用 1 NSString *path [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:archiver.plist];2 3 Dog *d [[Dog alloc] init];4 d.name 旺财;5 d.age 12;6 //归档7 [NSKeyedArchiver archiveRootObject:d toFile:path];8 //解档9 Dog *d1 [NSKeyedUnarchiver unarchiveObjectWithFile:path];
10 NSLog(name:%,d1.name);
11 NSLog(age:%ld,d1.age); 效果 更具体可以看github源码demo my github:https://github.com/alan12138/runtime 转载于:https://www.cnblogs.com/alan12138/p/5624314.html