#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对象做处理。这个时候,我们就是把对象当做参数传递。