@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. [MySQL]MySQL之权限管理

    一.MySQL权限简介 关于mysql的权限简单的理解就是mysql允许你做你全力以内的事情,不可以越界.比如只允许你执行select操作,那么你就不能执行update操作.只允许你从某台机器上连接m ...

  2. Selenium使用

    定位 1.普通 by id, name,class_name,link_text 2.加强 xpath css

  3. memcache的内存管理探微

    slab分配器:http://blog.csdn.net/luotuo44/article/details/42737181 hash操作  :http://blog.csdn.net/luotuo4 ...

  4. 【jQuery】--图片轮播

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Java笔记:修饰符

    Synchronized 修饰符 Synchronized 关键字声明的方法同一时间只能被一个线程访问.Synchronized 修饰符可以应用于四个访问修饰符. 实例 public synchron ...

  6. 文件服务器:FTP服务器详解

    文件服务器:FTP服务器 数据传输原理 功能简介 文件的传输与管理 不同等级的用户身份:user.guest.anonymous 实体用户.权限较完整 匿名用户.下载资源的能力 命令记录与日志文件记录 ...

  7. 书柜的尺寸(bzoj 1933)

    Description Tom不喜欢那种一字长龙式的大书架,他只想要一个小书柜来存放他的系列工具书.Tom打算把书柜放在桌子的后面,这样需要查书的时候就可以不用起身离开了.显然,这种书柜不能太大,To ...

  8. CrashMonkey4IOS App测试

    下载地址:https://github.com/vigossjjj/CrashMonkey4IOS 根据下载地址里面的说明安装一下,以下进行配置 1.进入CrashMonkey4IOS-master/ ...

  9. SQLServer

    # 将检索到的数据插入到一张新表 SELECT * INTO <NEW_TABLE_NAME> FROM <OLD_TABLE_NAME>

  10. ExtJs基础知识总结:自定义弹窗和ComboBox自动联想加载(四)

    概述 Extjs弹窗可以分为消息弹窗.对话框,这些弹窗的方式ExtJs自带的Ext.Msg.alert就已经可以满足简单消息提示,但是相对复杂的提示,比如如何将Ext.grid.Panel的控件显示嵌 ...