@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 ...
随机推荐
- Cookie, LocalStorage 与 SessionStorage
Cookie, LocalStorage 与 SessionStorage相同点 都是储存在用户本地的数据. 意义在于避免数据在浏览器和服务器间不必要地来回传递. 三者的特点 同属于html5 ...
- MyEclipse 常用快捷键
MyEclipse 常用快捷键 编辑: Ctrl+1 快速修复(最经典的快捷键,就不用多说了,可以解决很多问题,比如import类.try catch包围等) Ctrl+Shift+F 格式化当前代码 ...
- js三级地区联动
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head ...
- "_OBJC_CLASS_$_CMMotionManager", referenced from:
好久没写随笔了,今日项目爆红.如下: 缺少系统库 CoreMotion.framework, 在Build Phases -> Link Binary With Libraries 中添加即可.
- 史上最全的maven的pom.xml文件详解
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- JAVA的模式对话框和非模式对话框
周末的时候,一位网友让我帮他把他的无模式对话框改成有模式对话框. 界面是由swing制作的,都是JFrame,我从来没有接触过swing编程.大致的代码还是看的懂,很多都和C#很相似. 然后就去查资料 ...
- PHP反射API
近期忙着写项目,没有学习什么特别新的东西,所以好长时间没有更新博客.我们的项目用的是lumen,是基于laravel的一个轻量级框架,我看到里面用到了一些反射API机制来帮助动态加载需要的类.判断方法 ...
- scp 从远程服务器上一下载文件
scp -P202 xx3.x6.xx.xx:/usr/local/zookeeper-.zip /tmp #指定远程服务器的端口和远程服务器的目标文件 ,最后指定要下载到本的地目录 也可以从远程服务 ...
- HTML5 五子棋 - JS/Canvas 游戏
背景介绍 因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当 ...
- MachineKey 操作 之 获取 MachineKey
MachineKey获取介绍 对MachineKey进行配置,以便将其用于对 Forms 身份验证 Cookie 数据和视图状态数据进行加密和解密,并将其用于对进程外会话状态标识进行验证.本次讲的是如 ...