@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的非常规用法的更多相关文章

  1. Android线程管理(三)——Thread类的内部原理、休眠及唤醒

    线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...

  2. Android线程管理(三)——Thread类的内部原理、休眠及唤醒

    线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...

  3. ulua c#调用lua中模拟的类成员函数

    项目使用ulua,我神烦这个东西.lua单纯在lua环境使用还好,一旦要跟外界交互,各种月经不调就来了.要记住贼多的细节,你才能稍微处理好.一个破栈,pop来push去,位置一会在-1,一会在-3,2 ...

  4. C++ 模板的编译 以及 类模板内部的实例化

    在C++中.编译器在看到模板的定义的时候.并不马上产生代码,仅仅有在看到用到模板时,比方调用了模板函数 或者 定义了类模板的 对象的时候.编译器才产生特定类型的代码. 一般而言,在调用函数的时候,仅仅 ...

  5. 模拟窗口类ModelForm的应用

    模拟窗口类ModelForm的应用 模拟窗口是Form的窗口中的fields是引用models类 不知道窗口类,点击:https://www.cnblogs.com/guguobao/p/932202 ...

  6. 复习python的多态,类的内部权限调用 整理

    #多态的用法 class Dii: passclass Aii(Dii): def run(self): print('一号函数已调用')class Bii(Dii): def run(Dii): p ...

  7. 自定义异常类;键盘输入;try catch用法

    相关考点:自定义异常类:键盘输入:try catch用法 1.设计一个java程序,自定义一个异常类,从键盘输入一个字符串,如果等于“abc”,则抛出异常. public class MyExcept ...

  8. python类的内部方法

    目录 一.绑定方法与非绑定方法 1.绑定方法 2.非绑定方法 二.property 1.什么是property? 2.为什么要用property? 3.如何使用property? 三.isinstan ...

  9. 利用反射模拟一个spring的内部工作原理

    这个简单的案例是实行了登录和注册的功能,没有链接数据库.  在bean中id 是唯一的,id和name的区别在于id不能用特殊字符而name可以用特殊字符,比如:/-\.... package com ...

随机推荐

  1. 修改组策略,禁止用户修改IP

    运行中打开gepdit.msc,依次打开用户配置,管理模板,网络,网络连接:在右侧将“禁止访问LAN链接组建的属性”.“为管理员启用windows2000网络连接设置”设置为已启用即可令用户无法访问网 ...

  2. JSP复习整理(一)表单

    好久没更了,一周完成了SRDP,一周完成了课程设计,这一周就要好好回顾回顾Java Web的学习轨迹了. 用的eclipse Mars 一.表单 start.jsp <%@ page langu ...

  3. POJ 2533 Longest Ordered Subsequence LCS O(n*log(n))

    题目链接 最长上升子序列O(n*log(n))的做法,只能用于求长度不能求序列. #include <iostream> #define SIZE 1001 using namespace ...

  4. apk反编译工具

    反编译工具: apktool:资源文件获取,可以提取出图片文件和布局文件进行使用查看 dex2jar:将apk反编译成Java源码(classes.dex转化成jar文件) jd-gui:查看APK中 ...

  5. SSM框架总结(1)

    ----------------------20170106-------------------------------------- 1.mabatis.genertor中自动生成文件,需要在ge ...

  6. GroupJoin和Join的声明及调用

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable& ...

  7. 修改MySql 数据默认存储路径

    1. cmd进入控制台 net stop mysql 2.复制原来数据库目录到新目录 复制C:\ProgramData\MySQL\MySQL Server 5.5\中的data目录到 D:\Prog ...

  8. wamp2.5 配置多端口虚拟主机

    1.保证httpd.conf下 Include conf/extra/httpd-vhosts.conf LoadModule php5_module "D:/E/php/wamp/bin/ ...

  9. C#winForm调用WebService的远程接口

    Web Service 的创建简单编码.发布和部署 上一篇详细概述了WebService的创建,编码,发布和部署,那么作为客户端的程序如何访问远程端的WebService 接下来看一下具体步骤:   ...

  10. 防止sql注入和sqlmap介绍

    sql注入问题从WEB诞生到现在也一直没停过,各种大小公司都出现过sql注入问题,导致被拖库,然后存在社工库撞库等一系列影响. 防止sql注入个人理解最主要的就一点,那就是变量全部参数化,能根本的解决 ...