OC基础-day06

#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只能生成gettersetter方法的声明.

 

实现还要自己来.

 

 

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生成的settergetter方法的实现.

 

没有逻辑判断. 是直接赋值或者返回的.

 

 

如果你有自己的逻辑判断.那么重写这个方法就可以了.

 

@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. OC1门弱语言.

 

 

强语言: Java C#...

 

编译器在检查语法的时候,非常的严格. 是怎样就是怎样.

 

1点错误都不能犯.

swift严格到让人发指

 

弱语言优点:自由.灵活.想咋写就咋写.

缺点:出了错误以后,很难找. 错误只有在运行的时候发生.

强语言的缺点:不自由,必须按照语言的语法来写.

优点: 可以提前发现错误.

 

OC1门动态语言. 运行的时候才能确定一切.

 

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类型是1typedef类型的,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方法.

 

但是.方法的返回值是1MKPerson对象.

 

2. 如何解决上面的问题呢?

 

 

解决的方式: 就是把这个方法的返回值写成id.

 

这样的好处就是: 就算类型也不会做编译检查,不会给警告,因为id不会做编译检查.

 

 

+ (id)person

{

    return [MKPerson new];

}

 

存在的新问题:

虽然没有警告了.但是这个类方法无论如何返回的都是Person对象. 因为这个方法的实现就是创建1Pewrson对象返回.

 

新的需求: 这个类方法通过那1个类来调用就创建那1个类的对象.

 

3. 如何解决呢?

 

在类方法中创建对象的时候,类名不要写死了 而是写self

 

self代表当前类

 

这个类方法是通过那1个类来调用的.self就代表那1个类.

+ (id)person

{

    return [self new];

}

 

那个类来调用这个方法,创建的就是那1个类的对象.

 

还存在的问题:

 

这个时候,要接收这个类方法返回的对象的地址,实际上用任意类型的指针都是可以接的.

 

并且编译器连警告都不会有.

 

因为返回值是id类型的 id类型是不会做编译检查的. id1个无类型的指针.

新的需求: 希望这个类方法通过那1个类来调用.返回值就是那1个类的对象.

 

4. 如何解决呢?

解决方案: 将方法的返回值写成instancetype

 

 

代表: 返回值是当前类的对象. 这个方法通过那1个类去调用,就代表返回的是那个类的对象,

 

返回的地址是有类型的.

 

+ (instancetype)person

{

    return [self new];

}

 

这个时候,z这个类方法是通过那个类来调用的.

 

那么这个方法的返回值就是 这个类的对象. 返回值是有类型的地址.

 

这个方法创建的是 这个类的对象.

 

 

5. instancetypeid的区别

 

1). instancetype只能作为方法的返回值. 别的地方不能使用.代表返回值的类型为当前类的对象.

 

2), id1个万能指针,不仅可以作为方法的返回值. 也能声明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开头的方法 才叫做构造方法.

 

 

 

 

 

 

转载于:https://www.cnblogs.com/MKxiaoyue/p/5707651.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值