@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. 《转载》跟我学SpringMVC

    在线版目录 第一章 Web MVC简介 第二章 Spring MVC入门 第三章 DispatcherServlet详解 第四章 Controller接口控制器详解(1) 第四章 Controller ...

  2. iOS web remote debug 正确的姿势

    在使用iOS Remote debug需要做以下准备 1. iOS devices 开启java script and web inspector 开启方式如下: 2. mac OS 自带的Safar ...

  3. java--字符串

    一.基本数据类型 基本类型 大小 对应的包装类 最小值 最大值 byte 8-bit Java.lang.Byte -128 +127 short 2Byte= 16bit Java.lang.Sho ...

  4. ssl访问的原理

    本文无图文对照解释,但力求通俗易懂.请读者边读边手绘各个流程,一便于理解.      总体交互流程如下      1. 客户端发起HTTPS请求 这个没什么好说的,就是用户在浏览器里输入一个https ...

  5. MySQL NoInstall 配置

    1.下载 mysql-mysql-5.1.55-win32.zip 2. 解压缩到任何一个目录,最好目录名称不要有空格: 例如:C:\mysql 3. 删除Embedded,include,lib,m ...

  6. IOS进阶之WKWebView

    前言 Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等.因此,我们公司的应用也打算淘汰IOS7. 支持到 ...

  7. CSS样式表基础

    CSS的样式表其实就是美观页面的,加一些样式. 一.样式表的三种分类: ①内联样式:写在某一个标签里面的样式. 优点:控制精确. 缺点:代码重用性差.(太多了不好写)页面代码乱.(太乱,后期不方便看) ...

  8. Sql server2012还原备份文件语句

    --sql2012还原sql2008语句 --选择master数据库,新建查询 输入下面sql语句 --选择兼容模式(sql 2008)创建数据库db(还原时db写成原生数据库名称) RESTORE ...

  9. Linux内核--内核数据类型

    转自:http://www.linuxidc.com/Linux/2013-12/93637.htm 将Linux 移植到新的体系结构时,开发者遇到的若干问题都与不正确的数据类型有关.坚持使用严格的数 ...

  10. 杨氏矩阵:查找x是否在矩阵中,第K大数

    参考:http://xudacheng06.blog.163.com/blog/static/4894143320127891610158/ 杨氏矩阵(Young Tableau)是一个很奇妙的数据结 ...