OC基础-day02

时间:2021-08-15 07:42:49

#pragma mark - Day02_01_对象的创建与使用

1)如何通过类创建一个对象

1. 类是抽象的,无法直接使用

2. 对象是类的一个具体实现,可以直接使用

3. 语法

类名 *对象名 = [对象名   new];

2)如何使用对象

1. 类中有什么,对象中就有什么,类中有的属性,对象中都可以访问

2. 类中的成员变量,默认外界不能访问,我们需要加上修饰符@public

语法:

对象名 -> 属性名

还可以这么使用:(*对象名).属性名 ;

"扩展

属性:    通常是类暴露出来,外界可以访问或者修改(不一定真实存在)

成员变量: 类声明大括号里面的变量(对象类型和基本数据类型)

实例变量: 类声明大括号里面的变量(对象类型)

"练习

1.定义类

2.通过类来创建对象

3.使用对象

#pragma mark - Day02_02_犯错列表

1). 声明和实现 的 类名要一致.

2). @interface下面有1个大括弧,这个大括弧中才是写属性的.

3). 属性名一定要以下划线开头.

4). 属性是指针的时候,*要么和类型写在一起,要么和属性名写在一起,要么写中间,

5). 对象名其实是个指针变量,所以,对象的命名要遵守变量的命名规范.

6). 访问对象的属性的时候,虽然可以使用*去访问,但是你最好不要这么做,因为所有人都不这么做.都是使用->来访问的.

7). 通过对象名来访问对象的属性的时候,

对象名->属性名;

属性名是带了下划线的.

8). 默认情况下,对象的属性是不允许被外界访问的,应该加1个@public

9). 为对象的属性赋值,其实就是为对象的属性变量赋值,类型要和属性的类型一致.

10). 在类的外部不能直接操作属性,必须要创建对象,操作对象的属性.

11). 无论是为属性赋值,还是取属性的值, 都是操作的对象的属性,都要通过对象名来操作.

#pragma mark - Day02_03_无参数的方法的声明实现调用

1)方法定义

用来描述类共同的行为

类似于函数,只是跟语法不同

2)语法:

无参方法:

声明位置:放在类声明中{}的外面

- (返回值类型)方法名称;

- (void)run;

实现位置:放在类的实现中

- (void)run {

。。。。

}

调用 :

使用类的对象调用。

语法[对象名  方法名];

#pragma mark - Day02_04_带1个参数的方法的声明实现调用

1)声明

位置 同样是在.h的{}外面

语法 - (返回值类型)方法名:(参数类型)形参

2)实现

位置,同样是在.m中

语法,加上{}实现就行,并且直接可以使用形参

3)调用

[对象名  方法名:参数];

把对用的参数穿进去就可以了

#pragma mark - Day02_05_带多个参数的方法的声明实现调用

1)声明

位置 同样是在.h的{}外面

语法 - (返回值类型)方法名:(参数类型)形参1    :(参数类型)形参2    ......

2)实现

位置,同样是在.m中

语法,加上{}实现就行,并且直接可以使用形参

3)调用

[对象名  方法名:参数1   : 参数2];

#pragma mark - Day02_06_方法的命名规范

1)方法的名称

方法带参数,:也是方法的名字

- (void)run;

- (void)runWithSpeed:(double)speed;

- (void)runWithSpeed:(double)speed1 :(double)speed2;

- (void)runWithSpeed1:(double)speed1 speed2:(double)speed2;

2)命名规范

1.只有一个参数:  XXXXWith:参数   、XXXXWithXXXX:参数

2.多个参数:XXXwith:参数  and :参数

3.不要使用拼音

#pragma mark - Day02_07_同1个类的多个对象之间毫无关系(重点)

1) 同1个类,可以创建无数个对象.

对象可以创建很多个.所以你在访问属性的时候,必须要指定访问的是那1个对象的属性或者方法.

2) 每1个对象都有自己的属性.

每创建1个对象,这个对象中都有自己的属性和方法.

同1个类的多个对象之间毫无关系.

唯一的关系是:他们是根据同1个类模板创建出来的.

对象中具有相同的属性和方法.

但是对象的属性的值,各自是各自的 不相互影响.

#pragma mark - Day02_08_在方法的实现中直接访问属性(较难)

1) 在方法的实现中,可以直接访问本类的属性.

如果在方法中直接访问了属性的.

方法是通过对象来调用.方法是通过那1个对象来调用的.那么这个方法中访问的属性就是那1个对象的.

#pragma mark - Day02_09_案例演示

1)创建一个对象 Person

特征: 姓名 年龄 体重 智商

行为: 爬山 学习 吃饭 打游戏

声明

@interface HMPerson : NSObject

{

@public

NSString *_name;

int _age;

double _weight;

int _iq;

}

- (void)paShanWithLocation:(NSString *)location;

- (void)study;

- (void)eatWithFood:(NSString *)foodName;

- (void)playGame;

@end

实现

@implementation MKPerson

- (void)paShanWithLocation:(NSString *)location

{

//1,怕1次山.爬山的人的体重就减掉0.5Kg

NSLog(@"我登上了%@!",location);

//2.减.

_weight -= 0.5;

//3. 显示

NSLog(@"我的体重是%.2lf",_weight);

}

- (void)study

{

//每学习1此,智商+1;

NSLog(@"头悬梁,锥刺股");

//智商++;

_iq++;

//显示.

NSLog(@"学习完了以后我的智商是:%d",_iq);

}

- (void)eatWithFood:(NSString *)foodName

{

//1.先吃.

NSLog(@"主人.你给我的%@真好吃!",foodName);

//2.长体重.

_weight++;

//3.显示.

NSLog(@"吃完以后,我的体重是:%.2lf",_weight);

}

- (void)playGame

{

NSLog(@"魔兽毁一生,Doat穷三代!");

//智商--

_iq--;

NSLog(@"我的智商是:%d",_iq);

}

#pragma mark - Day02_10_类加载(难点、重点)

1) 内存中的五大区域.

栈: 存储局部变量.

堆: 允许程序员手动在堆区申请指定的连续的字节数的空间来使用.

BSS段: 存储未初始化的全局变量、静态变量.

数据段(常量区):  存储已经初始化的全局变量、静态变量、常量数据.

代码段:存储程序的代码.

2)类加载.

1. 当我们创建对象的时候,肯定需要访问这个类.因为只有访问了类才知道类中有那些成员.

2. 如果只是声明类指针的时候,也会访问这个类.以确定这个类型是否存在.

'当类第一次被访问的时候,会将类存储到代码段之中. 这个过程叫做类加载.

将类的代码存储在代码之中.

将类的代码以字符串的形式存储在代码段中.

'只有类第1次被访问的时候,才会有类加载.

一旦类被加载到代码区.直到程序结束的时候才会被回收.

#pragma mark - Day02_11_对象在内存中的存储

1. 类的本质是:我们自定义的数据类型.

MKPerson *p1 = [MKPerson new];

2. MKPerson *p1;  int *p;

这句话,仅仅是声明了1个指针变量而已.这个指针变量的类型是HMPerson*.

p1是1个局部的变量.所以p1指针变量是存储在栈区的.

p1是1个指针变量,所以这个变量中只能存储地址.

本质上来讲.p1是1个指针变量 不是1个对象.

3.[MKPerson new];

这句话,才是在真正的创建对象.

new做的事情.

a. 在堆内存中申请一块合适大小的空间.

b. 在申请的这块空间中根据类的模板创建对象.

类中有哪些属性.就把类的属性依次的挨个的一个不落的声明在这个对象中.

对象中除了有类中定义的属性之外,还有1个属性叫做isa 这是1个指针.

这个isa指针指向代码段中的类.

c. 初始化对象的属性.为对象的属性赋默认值

-> 如果属性的类型是基本数据类型.就赋值为0

-> 如果属性的类型是C指针类型.就赋值为NULL

-> 如果属性的类型是OC指针类型.就赋值为nil

d. 返回这个对象在堆空间中的地址.

将这个地址赋值给p1指针.

p1指针指向了堆空间中的HMPerson对象.

#pragma mark - Day02_12_对象在内存中的存储的细节

1) 注意

a. 对象中只有类的属性+isa的指针. 没有方法.isa指针指向了代码段中的类.

b. 如何访问对象的属性.

通过指针就可以找到指针指向的对象.找到对象了,就可以找到对象的属性.

p1->_name = @"jack";

c. 如何调用对象的方法?[p1 sayHi];

通过p1指针找到对象,发现是在调用方法.那么再根据对象的isa指针找到代码段中的类,再找到类中的对应的方法来执行.

d. 为什么方法不保存在对象中.

因为不管对象有多少个.方法的代码都是一样的.没有必要保存多份,只保存1份就可以了

e. 相同类的对象的isa指针的值一定都是一样的.

2) 对象的属性的初始值.

我们创建1个对象 如果没有为这个对象的属性赋值.那么这个对象的属性是有值的.

属性的类型是基本数据类型:  0

OC指针:      nil

C指针:       NULL

#pragma mark - Day02_13_nil值

1) C语言中学习的NULL.

a). NULL是1个值. 是C语言中指针变量的值.

b). 如果1个指针的值为NULL值,就代表这个指针不指向内存中的任何空间.

c). NULL本质上是1个宏.

define NULL ((void*)0)

所以 NULL 和 0 等价.

2) nil

a). nil也是1个值. 它也是1个指针变量的值.

b). nil的本质也是1个宏.

#define __DARWIN_NULL ((void *)0)

所以,nil和NULL完全是等价的.

c). 所以,如果你想要让1个指针不指向任何空间.

可以为这个指针变量赋值为NULL nil 0

3) 使用建议

a). 如果希望C指针不指向任何空间.我们一般为其赋值为NULL

b). 如果希望OC指针不指向任何空间,我们一般赋值nil

4) 注意问题

如果1个类指针的值为nil

HMPerson *p1 = nil;

代表p1指针不指向任何对象.

1. 所以这个时候 你通过p1去访问p1指向的对象的属性的时候,肯定的会报错.

2. 这个时候, 通过这个指针去调用方法的时候,

不会报错.也不会崩溃.只是没有任何反应的.

"扩展

void * 是C语言的万能指针

#pragma mark - Day02_14_多个指针指向同1个对象

"提问

int a = 10;

int *p1 = &a;

int *p2 = p1;

*p2 = 9;

a   的值是多少?

*p1 的值是多少?

1) 类型相同的OC类指针变量之间是可以相互赋值的

HMPerson *p1 = [HMPerson new];

HMperson *p2 = p1;

这个时候.p1和p2指针指向了同1个对象.

无论通过p1指针去访问对象.还是通过p2指针去访问对象,

访问的都是同1个对象.

2) 凡是你看到了new 就代表重新创建了1个对象.

[类名 new]; 就代表新创建了1个对象. 结果就是新创建的这个对象的地址.

[类名 new]; 其实啊,这是在调用1个方法.new是方法名称.

new方法的功能:创建对象.

new方法的返回值: 创建的对象的地址.

MKPerson *p1 = [MKPerson new];

p1->_name = @"李凯";

p1->_age = 17;

MKPerson *p2 = p1;

p2->_name = @"花花";

p2 = [MKPerson new];

NSLog(@"p1->_name = %@",p1->_name);

打印的值是“花花”

#pragma mark - Day02_15_分组导航标记

1) 存在的问题

当1个文件中的代码太多的时候,要找某1段代码.很难找.

2) 一种快速的方式查找文件中的代码.

Xcode 提供了导航条可以快速查找.

3) 分组导航标记.

1. #pragma mark 标记名.

2. #pragma mark -  就会产生1条分割线.

3. #pragma mark - 标记名.  就会产生1条分割线.并取1个标记名.

"扩展

自定义代码块

#pragma mark -  Day02_16_方法与函数

1) 之前在C语言中学习的叫做函数.

void test();

在OC类中定义的叫做方法.

- (void)sayHi;

2) 相同点:

1). 都封装1段代码,都表示1个相对独立的功能.

2). 函数/方法 如果不被调用,那么其中的代码是不会被执行.

3) 不同点.

1. 语法不同.

2. 定义的位置不一样.

a. 函数除了函数的内部和@interface的大括弧中不能定义.其他的地方都是可以定义的.

b. 而方法的声明只能在@interface的大括弧的外面,实现只能在@implementation中.

就算你把函数写在类中,这个函数也不是属于这个类的.

3. 归属感不同.

a. 函数就像是1个没有家的孩子,每1个函数是孤立的.

b. 方法是属于类的.类就是方法的家.

4. 调用形式不同.

a. 函数直接调用.

b. 方法必须要通过对象名来调用.

#pragma mark - Day02_17_都是你的错

创建类常见错误

1). @interface是类的声明. @implementation是类的实现 他们之间不能相互嵌套.

声明和实现是相对独立的 不能嵌套

2). 类的声明和实现必须都要有

3). 类必须要先声明然后再实现

4). @end不能省略

5). OC方法不能像函数那样直接调用 而是要创建对象 通过对象来调用

6). 成员变量不允许声明的同时初始化

7). 类的声明必须要放在使用类的前面.实现可以放在使用类的后面

8). 类语法上其实可以只有实现没有声明.

例如:

@implementation MKDog : NSObject

{

@public

NSString *_name;

int _age;

}

- (void)shout

{

NSLog(@"汪汪.....");

}

@end

虽然这样写是可以的,但是千万别这么写.很不规范.

9). 方法只有声明 没有实现

unrecognized selector sent to instance 0x100105a80

只要你看到了这个错误,就代表你调用的方法这个对象中没有 或者 没有实现.

实例--对象.

#pragma mark -  Day02_18_多文件开发

1) 需求,为什么要多文件开发?

如果将所有的类都写在main.m文件中,

那么类就会越来越多.canshufangfadedhihuyizhidyiduinahanchuanguolaipandaunchuanzhihhiihihchanfangfaebu

缺点

-> 这个文件就会很庞大.

-> 不容易维护.

-> 不利于团队开发.

2) 我们的一般做法.

1. 1个类独占1个模块.

1个模块至少分为两个文件

.h 文件.  h head 头文件. 在这个文件中我们写类的声明.

.m 文件   实现文件. 写上类的实现.

2. 什么时候要使用这个类,就只需要引入这个类模块的头文件就可以了.

3) 从现在开始.类不要再写在main.m中

而是应该独占1个模块.

.h  类的声明

.m  类的实现. 在.m中引入.h

4) NewFile --> cocoaClass --> 自动添加头文件和实现文件.并且还帮你把类的框架搭好.

5)  Xcode 可以自动生成前缀模板

#pragma mark - Day02_19_对象作为方法的参数(一)(重点)

1)对象与方法.

对象作为方法的参数.

对象作为方法的返回值.

2) 对象可以作为方法的参数.

因为类是我们自定义的数据类型.

1. 如何将对象作为方法的参数.

参数的类型写类的指针类型.

- (void)liuGouWith:(MKDog *)dog

2. 在调用这个方法的时候. 实参对象要和形参要求的一致.

3. 对象传递是 地址 传递.

传递完成以后,形参指针和实参指针其实指向的是同1个对象.

在方法的内部.通过形参指针访问对象的时候 其实啊 访问的就是实现指向的那个对象.

#pragma mark - Day02_20_对象作为方法的参数(二)

对象作为方法的返回值实际应用

1)首先创建一个人的类

有两个属性,一个是名字(name),另一个是剩余寿命(leftLife)

还有一个死的方法

再创建一个上帝类,名字(name),年龄(age)

有一个杀人的方法 (参数是人类型对象,把人的剩余寿命变为0)

声明:

@interface MKPerson : NSObject

{

@public

NSString *_name;

int _leftLife;

}

- (void)die;

@end

实现:

@implementation MKPerson

- (void)die

{

NSLog(@"救命啊.我叫%@.",_name);

NSLog(@"大不了,二十年后又是1条好汉.");

}

@end

上帝声明

#import <Foundation/Foundation.h>

#import "MKPerson.h"

@interface MKGod : NSObject

{

@public

NSString *_name;

int _age;

}

- (void)killWithPerson:(MKPerson *)per;

@end

实现

@implementation MKGod

- (void)killWithPerson:(MKPerson *)per

{

NSLog(@"孽畜:%@.我是%@,赶紧过来受死!",per->_name,_name);

per->_leftLife = 0;

[per die];

}

@end

在这里,通过上帝对象的killWithPerson:方法,我们把person对象传给上帝对象,上帝对象对person对象做处理。这个时候,我们就是把对象当做参数传递。