@dynamic 模拟NSManagedObject类的内部实现,AFN的非常规用法
@property和@synthesize复习
@property生成setter和getter的声明,同时生成属性对应的成员变量,并且前面加一个下划线_。如果将getter和setter的实现同时重写之后,它不会帮助生成属性对应的变量名。 (TestOne.m)这种写法,无法编译通过,因为_name已经不存在。
@interface TestOne : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation TestOne
- (void)setName:(NSString *)name {
_name = name; // Use of undeclared identifier '_name'
}
- (NSString *)name {
return _name; // Use of undeclared identifier '_name'
}
@end
比较特殊的一种情况是readonly的属性只会帮助生成getter的声明和变量。,如果当getter被重写之后,成员变量也不存在。
@interface TestTwo : NSObject
@property (readonly, nonatomic, copy) NSString *name;
@end
@implementation TestTwo
- (NSString *)name {
return _name; // Use of undeclared identifier '_name'
}
@end
如果在.h使用@property声明了方法属性,又想在.m重写方法怎么办呢。通常我们会使用@synthesize.
@synthesize是按照系统默认规则帮助生成getter和setter的实现,同时生成一个紧跟在关键字@synthesize后面的属性对应的成员变量,还可以更改属性对应的成员变量的名字。
同时如果使用了@synthesize之后,还可以继续重写getter和setter的实现,同时它帮助生成的成员变量依然存在。下面的代码是没有任何问题的。
@interface TestThree : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation TestThree
@synthesize name = _name; // 使用 @synthesize name 生成的成员变量是'name'
- (void)setName:(NSString *)name {
_name = name;
}
- (NSString *)name {
return _name;
}
@end
这时候@property的作用仅仅是相当于对外声明了方法原型:
- (void)setName:(NSString *)name;
- (NSString *)name;
但是不可以将.h中的@property (nonatomic, copy) NSString *name;替换为上面两句方法声明,因为@synthesize使用的前提是使用@property声明过这个属性。
对于readonly的属性,同样可以使用@synthesize关键字找到属性对应的成员变量名,然后重写getter就没有问题了。
@dynamic
我们都知道对应@dynamic修饰的属性,需要手动实现这个属性的getter和setter,否则虽然编译阶段能够通过,但是运行时会造成崩溃,错误信息为没有指定的方法。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
例如下面的代码
@interface TestFour : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation TestFour
@dynamic name;
@end
// 在main.m中
TestFour *testFour = [[TestFour alloc] init];
testFour.name = @"Mike"; //已经奔溃 [TestFour setName:]: unrecognized selector sent to instance
NSLog(@"%@", testFour.name);
这里就体现出了@synthesize和@dynamic的区别:
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法的实现。
@dynamic告诉编译器,属性的setter与getter方法需要实现,但是不会自动帮助生成。(当然对于readonly的属性只需提供getter实现即可)
@dynamic最常用的使用是在NSManagedObject中,此时不需要显示编程setter和getter方法。原因是:其getter和setter方法会在运行时动态创建,由Core Data框架为此类属性生成存取方法。那么CoreData究竟如何帮助NSManagedObject的子类生成子类属性的getter和setter实现的呢。我们大体可以模拟一下这个过程:
模拟CoreData NSManagedObject类的底层实现
根据OC运行时的特性,当子类的方法没有实现的实现,会去寻找父类的方法实现,为了语义上更好理解我使用Person和它的父类Super用来测试:(这其中Person模拟的是NSManagedObject的子类)
@interface Person : Super
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@dynamic name;
@end
@interface Super : NSObject
@end
@implementation Super
- (void)setName:(NSString *)name {
NSLog(@"执行了Super的setName:");
// setName ....
}
- (NSString *)name {
NSLog(@"执行了Super的name");
return @"Mike在Super中设置";
}
@end
Person *person = [[Person alloc] init];
person.name = @"Mike";
NSLog(@"%@", person.name);
因此上面的程序执行结果为:
执行了Super的setName:
执行了Super的name
Mike在Super中设置
那么问题就来了NSManagedObject是如何截获它子类的所有属性的getter和setter方法的调用,并完成代码实现的,毕竟它不会傻乎乎地把所有的属性都写一个getter和setter方法吧。虽然不知道它的具体实现方法,但是可以模拟一下这个过程。
这里提供一种利用消息转发机制来实现,主要用到两个方法- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector和- (void)forwardInvocation:(NSInvocation *)anInvocation:
methodSignatureForSelector:方法用来返回参数指定的SEL的指定的方法签名(方法签名是各种编程语言中通用的概念,主要是对方法的返回值类型、参数类型和参数的个数的描述,函数重载的概念就是同名方法具有不同的方法签名)。对于- (void)setName:(NSString *)name方法的方法签名就是:返回值为void类型参数个数为1参数类型为NSString。用自然语言描述是这样,如果用OC的方法编码描述就是:@"v@
@dynamic 模拟NSManagedObject类的内部实现,AFN的非常规用法的更多相关文章
- Android线程管理(三)——Thread类的内部原理、休眠及唤醒
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- Android线程管理(三)——Thread类的内部原理、休眠及唤醒
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- ulua c#调用lua中模拟的类成员函数
项目使用ulua,我神烦这个东西.lua单纯在lua环境使用还好,一旦要跟外界交互,各种月经不调就来了.要记住贼多的细节,你才能稍微处理好.一个破栈,pop来push去,位置一会在-1,一会在-3,2 ...
- C++ 模板的编译 以及 类模板内部的实例化
在C++中.编译器在看到模板的定义的时候.并不马上产生代码,仅仅有在看到用到模板时,比方调用了模板函数 或者 定义了类模板的 对象的时候.编译器才产生特定类型的代码. 一般而言,在调用函数的时候,仅仅 ...
- 模拟窗口类ModelForm的应用
模拟窗口类ModelForm的应用 模拟窗口是Form的窗口中的fields是引用models类 不知道窗口类,点击:https://www.cnblogs.com/guguobao/p/932202 ...
- 复习python的多态,类的内部权限调用 整理
#多态的用法 class Dii: passclass Aii(Dii): def run(self): print('一号函数已调用')class Bii(Dii): def run(Dii): p ...
- 自定义异常类;键盘输入;try catch用法
相关考点:自定义异常类:键盘输入:try catch用法 1.设计一个java程序,自定义一个异常类,从键盘输入一个字符串,如果等于“abc”,则抛出异常. public class MyExcept ...
- python类的内部方法
目录 一.绑定方法与非绑定方法 1.绑定方法 2.非绑定方法 二.property 1.什么是property? 2.为什么要用property? 3.如何使用property? 三.isinstan ...
- 利用反射模拟一个spring的内部工作原理
这个简单的案例是实行了登录和注册的功能,没有链接数据库. 在bean中id 是唯一的,id和name的区别在于id不能用特殊字符而name可以用特殊字符,比如:/-\.... package com ...
随机推荐
- 《转载》跟我学SpringMVC
在线版目录 第一章 Web MVC简介 第二章 Spring MVC入门 第三章 DispatcherServlet详解 第四章 Controller接口控制器详解(1) 第四章 Controller ...
- iOS web remote debug 正确的姿势
在使用iOS Remote debug需要做以下准备 1. iOS devices 开启java script and web inspector 开启方式如下: 2. mac OS 自带的Safar ...
- java--字符串
一.基本数据类型 基本类型 大小 对应的包装类 最小值 最大值 byte 8-bit Java.lang.Byte -128 +127 short 2Byte= 16bit Java.lang.Sho ...
- ssl访问的原理
本文无图文对照解释,但力求通俗易懂.请读者边读边手绘各个流程,一便于理解. 总体交互流程如下 1. 客户端发起HTTPS请求 这个没什么好说的,就是用户在浏览器里输入一个https ...
- MySQL NoInstall 配置
1.下载 mysql-mysql-5.1.55-win32.zip 2. 解压缩到任何一个目录,最好目录名称不要有空格: 例如:C:\mysql 3. 删除Embedded,include,lib,m ...
- IOS进阶之WKWebView
前言 Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等.因此,我们公司的应用也打算淘汰IOS7. 支持到 ...
- CSS样式表基础
CSS的样式表其实就是美观页面的,加一些样式. 一.样式表的三种分类: ①内联样式:写在某一个标签里面的样式. 优点:控制精确. 缺点:代码重用性差.(太多了不好写)页面代码乱.(太乱,后期不方便看) ...
- Sql server2012还原备份文件语句
--sql2012还原sql2008语句 --选择master数据库,新建查询 输入下面sql语句 --选择兼容模式(sql 2008)创建数据库db(还原时db写成原生数据库名称) RESTORE ...
- Linux内核--内核数据类型
转自:http://www.linuxidc.com/Linux/2013-12/93637.htm 将Linux 移植到新的体系结构时,开发者遇到的若干问题都与不正确的数据类型有关.坚持使用严格的数 ...
- 杨氏矩阵:查找x是否在矩阵中,第K大数
参考:http://xudacheng06.blog.163.com/blog/static/4894143320127891610158/ 杨氏矩阵(Young Tableau)是一个很奇妙的数据结 ...