
#pragma mark - Day06_01_点语法
1. 点语法.
1). 如果要访问对象的属性,还要去调用属性对应的setter getter方法.好烦躁好烦躁.
2). 点语法的作用: 快速调用属性的getter setter方法.
3). 语法:
对象名.去掉下划线属性名;
如果是赋值,就自动调用这个属性对应的setter方法.
如果是取值,就自动调用这个属性对应的getter方法.
2. 使用点语法快速的调用setter方法为属性赋值
对象名.去掉下划线的属性名 = 数据;
MKPerson *p1 = [MKPerson new];
p1.name = @"小明"; //不是把@"小明"直接赋值给p1对象的_name属性
原理: 编译器在编译的时候,其实就把这个点语法替换为了调用setter方法的代码.
替换的规则:
对象名.属性名 = 数据;
会替换成如下代码:
[对象名 set首字母大写的属性名:数据];
p1.name = @"小明";
[p1 setName:@"小明"];
所以,使用点语法为属性赋值.本质上还是调用的setter方法.
3. 使用点语法快速调用属性的getter方法取出属性的值.
格式: 对象名.去掉下划线的属性名;
NSString *name = p1.name;
本质上 是调用p1对象的_name属性的getter方法.getter方法的返回值就是取到的值.
编译器在编译的时候. 其实就把这个点语法转换为了调用getter方法的代码.
对象名.去掉下划线的属性名;
[对象名 去掉下划线的属性名];
NSString *name = p1.name;
转换: NSString *name = [p1 name];
4. 点语法是1个编译器特性.
编译器在编译的时候,其实就已经把点语法转换为了调用getter setter方法的代码了.
如果是通过点语法赋值.那么就转换为调用setter方法的代码.
如果是通过点语法取值.那么就转换为调用getter方法的代码.
所以 点语法的本质还是调用getter setter方法
点语法的语法:
要为属性赋值: 对象名.去掉下划线的属性名 = 数据.
要取出属性的值: 对象名.去掉下划线的属性名;
5. 几个使用注意
1). 在getter 和 setter方法中, 慎用点语法.
一不小心 就是死循环.
点语法的本质就是调用getter setter
2). 点语法的本质是调用getter setter
所以.你的属性如果没有封装getter setter 自然是使用不了点语法的.
3). 点语法是在编译器编译的时候,转换为调用对应的getter setter方法.
如果你的getter setter方法的名字不符合规范的话.
点语法也是无法使用的.
因为编译器在转换的时候.是按照规范来转换的.
对象名.去掉下划线的属性名 = 数据;
[对象名 set去掉下划线的属性名并且首字母大写:数据];
对象名.去掉下划线的属性名;
[对象名 去掉下划线的属性名];
#pragma mark - Day06_02_@property的使用
1. @property
1). 作用: 自动生成getter setter方法的声明.
因为是自动生成etter sette方法的声明 所以,应该写在@interface中.
2). 语法:
@property 数据类型 名称;
@property NSString *name;
原理:在编译器编译的时候.会根据这个@property自动的生成getter setter方法的声明.
生成方法的规则:
@property 数据类型 名称;
a. 先生成setter方法的声明
- (void)set名称首字母大写:(数据类型)名称;
b. 再生成getter方法的声明.
- (数据类型)名称;
@property int age;
- (void)setAge:(int)age;
- (int)age;
2. 使用@property注意.
1). @property的类型要和属性的类型一致.
2). @property的名称要和属性的名称一致(只是去掉下划线)
3). 一定要按照这个规则来.只有这样它自动生成的getter setter方法名称才符合我们的规范.
4). @property可以批量声明.
当@property的类型相同的时候,可以批量声明.
类型不同是不可以批量声明的.
5). @property只是生成getter setter的声明.
实现还要自己去实现;
#pragma mark - Day06_03_@synthesize的使用
1. @property只能生成getter、setter方法的声明.
实现还要自己来.
2. @synthesize.
1). 作用: 自动生成getter setter方法的实现.
所以,它是写在@implementation中.
2). 使用格式:
@synthesize @pro名称;
注意,这个@synthesize后面的名称必须要是@interface中的@property的名称;
3). @synthesize的原理.
a. 先自动的生成1个私有属性.类型和@synthesize对应的@pro类型一致.
名称和@pro的名称一致. 和@pro的名称一致. 不会有下划线.
这个属性是1个真私有的属性,也就是声明在@implementation中.
b. 自动的生成setter方法的实现.
这个方法的内部的实现是如何实现的?
这个方法的内部什么都没有做. 直接将参数赋值给了它自动生成的那个私有属性.
c. 自动的生成getter方法的实现.
这个方法的内部的实现是如何实现的?
直接返回生成的那个私有属性的值.
#pragma mark - Day06_04_@synthesize的使用注意
1. 虽然@synthesize会自动的生成属性.
我认为他生成的这个属性的名称是很不符合规范.
所以,我希望@synthesize不要去自动生成私有属性.并且在getter setter的方法实现中操作的属性是已经存在的属性.
语法格式:
@synthesize @pro名称 = 已经存在的属性名;
@synthesize name = _name;
意义:
1). 不会再去自动生成私有属性了.
2). 自动生成setter方法的实现.
直接将参数的值赋值给后面指定的属性.
3). 自动生成getter方法的实现.
直接将返回后面指定的属性的值.
4. 使用注意.
@synthesize生成的setter、getter方法的实现.
没有逻辑判断. 是直接赋值或者返回的.
如果你有自己的逻辑判断.那么重写这个方法就可以了.
@synthesize仍然可以批量声明.
类型不同,也可以批量.
#pragma mark - Day06_05_@property的增强使用
1. 我们刚才讲的@property @synthesize 这些用法是在Xcode4.4之前的用法.
从Xcode4.4开始. 苹果对property做了1个增强
从4.4以后.只要写1个@property. 编译器就会自动的帮助你完成如下工作
1). 会自动的帮助你生成1个带下划线的、类型和@pro类型一致的属性.
2). 会自动的帮助你生成这个属性的getter setter方法声明.
3). 会自动的帮助你生成这个属性的getter setter方法实现.
2. @property的原理.
@interface MKPerson : NSObject
@property NSString *name;
@end
@implementation MKPerson
@end
---------------编译器在编译的时候-----------
@interface MKPerson: NSObject
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
@implementation MKPerson
{
NSSteing *_name;
}
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
@end
3. @property做的事情.
1). 生成1个和@pro类型相同、名称加1个下划线的私有属性.
这个属性是生成在@implementation中的.
2). 自动生成这个私有属性的getter setter方法的声明.
3). 自动生成这个私有属性的getter setter方法的实现.
setter如何实现: 直接将参数的值赋值给生成的那个私有属性
getter如何实现: 直接返回生成的那个私有属性的值;
#pragma mark - Day06_06_使用@property增强注意
1. 使用@property要注意的几个问题.
1). @property的类型要和需要生成的属性的类型一致.
@pro的名称要和想要生成的属性的名称一致.去掉下划线.
2). @property也是可以批量声明的. 前提是类型相同的情况下.
3). @property生成的方法实现是没有任何逻辑验证的.
如果你有逻辑验证,可以自己重写.
如果你重写了setter方法 @property仍然会自动生成私有属性 和 getter 方法.
如果你重写了getter方法.@property仍然会自动生成私有属性 和 setter 方法.
通过你同时重写了getter setter,那么@property就不会自动生成私有属性了.
5. 从今往后.我们不再为类声明属性、不再写getter setter方法的声明 不再写getter setter方法的实现.
如下几个情况除外.
1). 属性不需要被外界访问.
2). 有自己的逻辑验证;
"补充
如果@property使用位置不是在@interface{}外面(分类/协议),那么@property所做的事情会不一样
"练习
定义一个person类
属性:姓名 年龄 性别
使用@property声明属性(不要批量声明)
并且在main函数中使用点语法,设置数值和获取数值;
#pragma mark - Day06_07_任意的指针可以指向任意的对象
1. OC是1门弱语言.
强语言: Java C#...
编译器在检查语法的时候,非常的严格. 是怎样就是怎样.
1点错误都不能犯.
swift严格到让人发指
弱语言优点:*.灵活.想咋写就咋写.
缺点:出了错误以后,很难找. 错误只有在运行的时候发生.
强语言的缺点:不*,必须按照语言的语法来写.
优点: 可以提前发现错误.
OC是1门动态语言. 运行的时候才能确定一切.
2. 动态类型和静态类型.
1). 静态类型: 指针指向的对象是1个本类对象.
2). 动态类型: 指针指向的不是1个本类对象
所以,在OC中 任意的指针可以指向任意的对象.
编译器最多只是给1个警告而已.
当指向的是1个本类对象,或者子类对象的时候,编译器不会给警告.
要存储1个对象的地址,其实任意类型的指针变量都无所谓的啦.
因为指针变量都是占据8个字节. 无论什么类型都是这样的.
#pragma mark - Day06_08_编译检查
编译检查.
编译检查:是编译器在编译源代码的时候,主要是检查代码是否符合语法规范吧.
LLVM
1) 对象类型之间可以相互转换类型,但是对象和基本数据类型不可以强行转换
2) 简而言之,编译检查就是检查变量'声明的类型'是否存在我们想要的操作,如果有,就会编译通过.
MKPerson *p1 = [MKStudent new];
[p1 sayHi];
[(MKStudent *)p1 study];
#pragma mark - Day06_09_运行检查
1. 运行检查.
程序在运行的时候.当要调用1个指针指向的对象的方法的时候.
仍然会做1个检查,会去检查,这个指针指向的对象中是否真的有这个方法,如果真的有,就指向,如果没有,就运行报错.
这就是运行检查.
[p1 sayHi]; 调用p1指针指向的对象的sayHi方法.
所以:
1). 编译检查只是检查指针的类型.
2). 运行检查才会真正的去检查对象中是否有这个方法.
#pragma mark - Day06_10_NSObject万能指针
1. NSObject指针.
根据我们的里氏替换原则,NSObject类是所有OC类的祖宗类.
NSObject指针可以指向任意的OC对象.
注意的是: C中的数据类型不是OC对象.
当NSObject指针指向子类对象的时候, 如果要调用子类对象的独有成员.就必须做类型转换.
所以:
1). NSObject指针是1个万能指针.
2). 当指针的类型是NSObject*类型的时候.编译器要做编译检查.
#pragma mark - Day06_11_id指针
.id指针.
1). id类型是1个typedef类型的,在typedef的时候,已经加*了.
所以 声明id类型的指针不需要再加*
2). id指针是1个万能指针.它可以指向任意的OC对象.
3). 和NSObject指针的区别在于:
a. 当指针的类型是NSObject类型的时候. 编译器在编译的时候,会做编译检查
b. 当指针的类型是id类型的时候,不会做编译检查,直接通过
当编译器在编译的时候,如果发现指针的类型是id类型的.
这个时候,直接通过.不会做任何的编译检查.
4). id指针的局限性: 只能使用中括弧调用方法.不能使用点语法.
"补充
属性的名称可以叫id 不会出现问题
#pragma mark - Day06_12_id指针总结
id指针 总结
1). NSObject指针和id指针都是叫做万能指针.
他们都可以指向任意的OC对象.
2). 通过指针去调用方法的时候.
a. 如果指针的类型是NSObject类型的时候,编译器在编译的时候,会做编译检查.
如果就是要调用. 必须要做类型转换,
b. 如果指针的类型是id类型的时候 编译器在编译的时候,会直接通过. 不用做类型转换.
3). 只是在编译的时候不会做编译检查.
但是运行的时候,仍然要做运行检查.
#pragma mark - Day06_13_instancetype
1. 在父类中写1个类方法,返回1个对象
+ (MKPerson *)person
{
return [MKPerson new];
}
这么写存在的问题:
子类可以继承这个类方法. 通过子类也可以调用这个person方法.
但是.方法的返回值是1个MKPerson对象.
2. 如何解决上面的问题呢?
解决的方式: 就是把这个方法的返回值写成id.
这样的好处就是: 就算类型也不会做编译检查,不会给警告,因为id不会做编译检查.
+ (id)person
{
return [MKPerson new];
}
存在的新问题:
虽然没有警告了.但是这个类方法无论如何返回的都是Person对象. 因为这个方法的实现就是创建1个Pewrson对象返回.
新的需求: 这个类方法通过那1个类来调用就创建那1个类的对象.
3. 如何解决呢?
在类方法中创建对象的时候,类名不要写死了 而是写self
self代表当前类
这个类方法是通过那1个类来调用的.self就代表那1个类.
+ (id)person
{
return [self new];
}
那个类来调用这个方法,创建的就是那1个类的对象.
还存在的问题:
这个时候,要接收这个类方法返回的对象的地址,实际上用任意类型的指针都是可以接的.
并且编译器连警告都不会有.
因为返回值是id类型的 而id类型是不会做编译检查的. id是1个无类型的指针.
新的需求: 希望这个类方法通过那1个类来调用.返回值就是那1个类的对象.
4. 如何解决呢?
解决方案: 将方法的返回值写成instancetype
代表: 返回值是当前类的对象. 这个方法通过那1个类去调用,就代表返回的是那个类的对象,
返回的地址是有类型的.
+ (instancetype)person
{
return [self new];
}
这个时候,z这个类方法是通过那个类来调用的.
那么这个方法的返回值就是 这个类的对象. 返回值是有类型的地址.
这个方法创建的是 这个类的对象.
5. instancetype和id的区别
1). instancetype只能作为方法的返回值. 别的地方不能使用.代表返回值的类型为当前类的对象.
2), id是1个万能指针,不仅可以作为方法的返回值. 也能声明id类型的指针变量.
#pragma mark - Day06_14_在对象方法中使用self创建当前类的对象
在那个类中调用 self 就代表那个类,所以可以直接使用self创建当前对象
[[self class] new]
#pragma mark - Day06_15_动态类型检测
1. 编译检查和运行检查.
就算通过了编译检查,运行不一定会成功的.
需求: 编译检查只是检查指针的类型.
我们希望检查1下这个指针指向的对象中到底有没有这个方法
如果有 我才去调用. 如果没有就不要调用.
2. 判断指针指向的对象中是否真的有指定的方法
- (BOOL)respondsToSelector:(SEL)aSelector; ******
使用这个方法就可以避免我们的运行错误.
可以判断对象中有没有这个方法、这个方法有没有实现.
- (BOOL)isKindOfClass:(Class)aClass;
判断指定的对象是否为指定类的对象或者指定类的子类对象.
- (BOOL)isMemberOfClass:(Class)aClass;
判断指定的对象是否为指定类的对象,不包括子类.
只能判断本类对象,不包括子类.
+ (BOOL)isSubclassOfClass:(Class)aClass;
判断类是否为另外1个类的子类.
#pragma mark - Day06_16_构造方法简介
1. 创建1个类的对象,那么就调用这个类的new方法就可以.
new方法是1个类方法
做的事情:创建类的对象,并且初始化这个对象.
返回值: 把这个对象的地址返回.
new方法的内部做的事情.
这个方法的内部什么事情都没有做.
就是是调用了.
alloc方法和init方法.
alloc方法:
首先是1个类方法.这个方法做的事情: 就是创建对象.
init方法
是1个对象方法 这个方法做的事情:初始化这个对象.
初始化:
就是为对象的属性赋默认值
所以,创建对象我们也可以这么创建.
MKPerson *p2 = [[MKPerson alloc] init];
MKPerson *p2 = [MKPerson new];
因为我们刚刚说过. new方法的内部就是先调用的alloc 再调用的init
init方法 我们叫做构造方法.
init方法做的事情: 为对象的属性赋默认值 初始化对象.
init方法做的事情,不仅仅是为对象的属性赋默认值 还要做的别的事情.
创建的对象务必要调用init方法初始化以后才可以使用,否则就有可能会出问题.
#pragma mark - Day06_17_重写init方法
1. init方法就是我们的构造方法.
为什么对象一创建出来,对象的属性的默认值就是:
0 nil NULL.
因为在构造方法中,为这些对象的属性初始化了.
init方法做了很多事情,其中1件事情就是为当前对象的属性赋默认值.
属性的类型是基本数据类型 0
C指针 NULL
OC指针 nil
3. 如果我们希望对象创建出来以后,对象的属性的默认值不是0 nil NULL
而是别的值.
那么我们就可以重写init方法.
重写init的方法的规范.
1). 要先调用父类的init方法.
因为父类的init方法不仅仅是初始化属性的值,还做了别的事情.
这些事情是要做的,所以要调用1下父类的init方法
2). init方法有1个返回值.返回的是当前对象.
调用init方法的时候,有可能会执行失败.
如果执行失败返回的就是nil.
调用父类的init方法. 将这个方法的返回值赋值给self
3). 判断父类的init方法有没有执行成功.
如果成功,再去初始化子类的属性. 按照自己的方式.
4). 返回当前对象. self
- (instancetype)init
{
self = [super init];
if(self != nil)
{
初始化子类属性 .
}
return self;
}
总结:
1). 什么时候需要重写init方法?
a. 对象一创建出来,不希望对象的属性的默认值是 0 nil NULL 而是我指定的值.
b. 如果你有1段代码,想在对象创建的同时执行,那么就可以将这个代码写在init方法中
2), 重写init方法的规范:
必须要先调用父类的init方法.并且赋值给self.然后判断成功.
- (instancetype)init
{
if(self = [super init]) //说明父类的init方法执行成功
{
初始化子类自己的属性.
}
return self;
}
#pragma mark - Day06_18_自定义构造方法
重写init方法以后.这个时候创建出来的所有的对象的属性的值都是一模一样的.
需求: 对象的属性的值 是由用户传进来的.
自定义构造方法.
自定义构造方法的步骤
1). 肯定是1个对象方法.
2). 返回值写 instancetype
3). 方法名必须以initWith开头.
自定义构造方法必须要以initWith开头.
因为self只能在构造方法中赋值.
编译器只认为 init 或者以initWith开头的方法 才叫做构造方法.