<a>目录
<a>对象在内存中的储存
nil 与 NULL
多个指针指向同一个对象
方法
最容易犯错
对象与方法
对象作为类的属性</a>
对象在内存中的储存
-
内存中的五大区域.
栈 存储局部变量.
堆 程序员手动申请的字节空间 malloc calloc realloc函数.
BSS段 存储未被初始化的全局变量 静态变量.
数据段(常量区) 存储已被初始化的全局 静态变量 常量数据.
代码段 存储代码. 存储程序的代码. -
类加载.
1). 在创建对象的时候 肯定是需要访问类的.
2). 声明1个类的指针变量也会访问类的.
-
在程序运行期间 当某个类第1次被访问到的时候. 会将这个类存储到内存中的代码段区域.这个过程叫做类加载.
-
只有类在第1次被访问的时候,才会做类加载.
一旦类被加载到代码段以后. 直到程序结束的时候才会被释放.
-
对象在内存中究竟是如何存储的.
假设下面这个写在函数之中.
Person *p1 = [Person new];1). Person *p1; 会在栈内存中申请1块空间. 在栈内存中声明1个Person类型的指针变量p1。
p1是1个指针变量. 那么只能存储地址.2). [Person new]; 真正在内存中创建对象的其实是这句代码.
new做的事情 a. 在堆内存中申请1块合适大小的空间. b. 在这个空间中根据类的模板创建对象. 类模板中定义了什么属性.就把这些属性依次的声明在对象之中. 对象中还有另外1个属性 叫做isa 是1个指针. 指向对象所属的类在代码段中的空间. (或者说代表着对象所属的类在代码段中的地址) c. 初始化对象的属性 如果属性的类型是基本数据类型 那么就赋值为0 如果属性的类型是C语言的指针类型 那么就赋值为NULL 如果属性的类型是OC的类指针类型 那么就赋值为nil d. 返回对象的地址.
3). 注意
a. 对象中只有属性,而没有方法. 自己类的属性外加1个isa指针指向代码段中的类. b. 如何访问对象的属性 指针名->属性名; 根据指针 找到指针指向的对象 再找到对象中的属性来访问. c. 如何调用方法. [指针名 方法名]; 先根据指针名找到对象,对象发现要调用方法 再根据对象的isa指针找到类. 然后调用类里的方法.
-
为什么不把方法存储在对象之中?
因为每1个对象的方法的代码实现都是一模一样的 没有必要为每1个对象都保存1个方法 这样的话就太浪费空间了.
既然都一样 那么就只保持1份. -
对象的属性的默认值.
如果我们创建1个对象,没有为对象的属性赋值. 那么这个对象的属性是有值的.
如果属性的类型是基本数据类型 默认值是0
如果属性的类型是C指针类型 那么默认值是NULL
如果属性的类型是OC指针类型 那么默认值是nil
nil 与 NULL
-
NULL
只能作为指针变量的值. 如果1个指针变量的值是NULL值. 代表这个指针不指向内存中的任何1块空间
NULL其实等价于0 NULL其实是1个宏. 就是0
-
nil
只能作为指针变量的值. 代表这个指针变量不指向内存中的任何空间.
nil其实也等价于0 也是1个宏. 就是0.所以, NULL和nil其实是一样的 。
-
使用建议.
虽然使用NULL的地方可以是nil 使用 nil的地方可以使用NULL 但是不建议大家去随便使用.
C指针用NULL
int *p1 = NULL; p1指针不指向内存中的任何1块空间.OC的类指针用nil
Person *p1 = nil; p1指针不指向任何对象.
-
如果1个类指针的值为nil 代表这个指针不指向任何对象.
Person *p1 = nil;那么这个时候 如果通过p1指针去访问p1指针指向的对象的属性 这个时候会运行报错.
那么这个时候,如果通过p1指针去调用对象的方法 运行不会报错 但是方法不会执行 没有任何反应
多个指针指向同一个对象
- 同类型的指针变量之间是可以相互赋值的.
Person *p1 = [Person new];
Person *p2 = p1; 这是完全没有问题的. p1和p2的类型都是Person指针类型的.
代表将p1的值赋值给p2 而p1的值是对象的地址 所以就把对象的地址赋值给了p2
所以p1和p2指向了同1个对象.
无论通过p1还是p2去修改对象,修改的都是同1个对象.
目前为止,只要你看到new 就说明新创建了1个对象.
方法
- 我们之前在C中学习的函数,就叫做函数.
void test()
{
}
- 在OC类中写的方法.就叫做方法.
- (void)sayHi;
-
相同点:
都是用来封装1段代码的. 将1段代码封装在其中, 表示1个相对独立的功能
函数或者方法只要被调用.那么封装在其中的代码就会被自动执行. -
不同点:
1). 语法不同.
2). 定义的位置不一样.
OC方法的声明只能写在@interface的大括弧的外面,实现只能写在@implementation之中.
函数除了在函数的内部和@interface的大括弧之中 其他的地方都是可以写.* 就算把函数写在类中 这个函数仍然不属于类 所以创建的对象中也没有这个函数. 注意: 函数不要写到类中.虽然这样是可以的 但是你千万不要这么做 因为这么做是极度的不规范的.
3). 调用的方式也不一样.
函数可以直接调用. 但是 方法必须要先创建对象 通过对象来调用.
4). 方法数是属于类的. 有1个伐木累. 有个归属感.
而函数是1个孤魂野鬼, 就是1个独立的.
最容易犯错
1). @interface是类的声明. @implementation是类的实现 他们之间不能相互嵌套
2). 类必须要先声明然后再实现
3). 类的声明和实现必须都要有 就算没有方法 类的实现也不必不可少的.
4). 类的声明必须要放在使用类的前面
实现可以放在使用类的后面
5). 声明类的时候 类的声明和实现必须要同时存在.
特殊情况下可以只有实现 没有声明.
虽然可以这样,但是我们平时在写类的时候千万不要这么写 因为这么写是极度不规范的.
6) 属性名一定要以下划线开头 这是规范.
类名 每1个单词的首字母大写.
7). 属性不允许声明的时候初始化
在为类写1个属性的时候 不允许在声明属性的时候为属性赋值.
8). OC方法必须要创建对象通过对象名来调用
9). 方法只有声明 没有实现
a. 如果方法只有声明 没有实现 编译器会给1个警告 不会报错.
b. 如果指针指向的对象 有方法的声明 而没有方法的实现 那么这个时候通过指针来调用这个方法
在运行的时候 就会报错.
unrecognized selector sent to instance 0x100420510
只要你看到了这个错误.说明要么对象中根本就没有这个方法. 要么只有方法的声明而没有方法的实现.
对象与方法
-
对象和方法.
对象作为方法的参数,对象作为方法的返回值.
-
类的本质是我们自定义的1个数据类型. 因为对象在内存中的大小是由我们决定的.
多写几个属性 对象就大一些 少写几个属性 对象占用的空间就小一些.什么是数据类型: 是在内存中开辟空间的1个模板.
-
既然类是1个数据类型,那么类就可以作为方法的参数.
- (void)test:(Dog *)dog;
这个是完全没有问题的.
-------语法注意-------------
- 当对象作为方法的参数的时候. 参数应该怎么写 参数类型是类指针,
- (void)test:(Dog *)dog;
-
调用方法的时候 如果方法的参数是1个对象.
那么给实参的时候,实参要求也必须是1个符合要求的对象. 否则就会出问题.
-
当对象作为方法的参数传递的时候,是地址传递.
所以 在方法内部通过形参去修改形参指向的对象的时候 会影响实参变量指向的对象的值.
对象也可以作为方法的返回值.
1.什么时候方法的返回值是1个对象呢?
当方法执行完毕之后,如果有1个对象方法的内部不知道如何处理.并且这个对象是调用者翘首以盼的.
那么这个时候我们就应该将这个对象返回.
2.如果方法的返回值是1个对象,那么返回值类型应该写 类指针.
对象作为类的属性
-
对象作为类的属性.
1). 类的属性代表什么?
代表类所拥有的东西. 设计类的时候.我们怎么知道这个类有哪些属性呢?分析这类事物拥有什么东西.
拥有的东西就可以作为这个类的属性.人有姓名、年龄、性别、身高、钱. 空调类:品牌、颜色、大小、匹数.
2). 类的方法代表什么?
代表这类事物具有的行为.具有的功能.
设计类的时候.我们如何知道这个类有哪些方法呢?
那么就分析这类事物具有哪些行为,这个类事物具有哪些功能,会做什么?人: 走路 吃饭 睡觉 唱歌.. 空调: 制冷.制热.
-
有1个人类,还有1个狗类.
人养了一条狗,人拥有1条狗.
如何描述这个关系:人拥有1条狗.人拥有1条狗,那么就把狗作为人的1个属性.
-
对象完全可以作为另外1个类的属性.代表这个类拥有这个对象.
A类拥有1个B对象,那么就将B对象作为A类的属性.
-
当A类作为B类的属性的时候. 在创建B类对象的时候, B类对象中的属性仅仅只是1个指针变量.
默认情况下并没有创建A对象. -
重点:
1). 什么时候1个类可以作为另外1个类的属性.2). 属性的本质其实是1个变量.
3). 如何访问对象的属性 对象的成员.
网友评论