内存中的五大区域
栈 存储局部变量
堆 程度员手动申请的字节看空间 malloc calloc realloc函数
BSS段 存储未被初始化的全局变量, 静态变量
数据段(常量区) 存储已被初始化的全局、静态变量、常量数据
代码段 存储代码 存储代码程序
类加载
a. 在创建对象的时候 肯定需要访问类
b. 声明1个类的时候指针变量也会访问类
类加载: 在程序运行期间,当某个类第1次被访问的时候就会把类加载到代码段
只有类在第1次被访问的时候,才会做类加载. 一旦类被加载到代码段后,直到程序结束的时候才会被释放
-
对象在内存中究竟是如何存储的
假设下面这个写在函数中
Person *p1 = [Person new];
- Person *p1 : 会在栈内存中申请1空间,在栈内存中声明1个Person类型的指针变量,
p1是指针变量,只能存储地址
- new做的事情:
a. 在堆内存中申请1块合适大小的空间
b. 在这个控件中根据类的模板创建对象
依次声明属性,对象还有额外的一个属性,叫isa是一个指针。
isa指向所属的类在代码段当中的地址.
c. 初始化对象的属性
如果属性的类型是基本数据类型,就赋值为0;
如果属性的类型是C语言的指针类型,就赋值为NULL;
如果属性的类型是OC语言的指针类型,就赋值为nil;
d. 返回对象的地址
- 注意
a. 对象当中只有属性,而没有方法, 自己类的属性外加1个isa指针指向代码段中的类
b. 如何访问对象的属性
指针名->属性名;
根据指针 找到指针指向的对象 再找到对象中的属性来访问
c. 如何调用方法
[指针名 方法名];
先根据指针名找到对象,对象发现要调用方法 再根据对象的isa指针找到类,然后调用类里的方法
为什么不把方法存储在方法之中
因为每个对象的方法实现代码都是一模一样的,所以只保存一份来节省空间
对象属性的默认值
如果我们创建一个对象,每一为对象的属性赋值,这个对象是有值的。
如果属性的类型是基本数据类型,就赋值为0;
如果属性的类型是C语言的指针类型,就赋值为NULL;
如果属性的类型是OC语言的指针类型,就赋值为nil;
-
NULL与nil
1). NULL
只能可以作为指针变量的值,如果1个指针变量的值是NULL代表这个指针不指向内存中的任何一块空间,NULL其实等价于0.是一个宏
#define NULL ((void *)0)
2). nil
只能作为指针变量的值. 代表这个指针不指向内存中的任何空间.
nil也等价于0 也是一个宏
所以NULL和nil是一样的
3). 使用建议
a. C指针用NULL值
int *p = NULL;
b. OC的类指针用nil
Person *p = nil;
4). 如果1个类指针的值为nil,代表这个指针不指向任何对象
Person *p = nil;
不能向它的属性赋值, 对象的方法不会执行.
同类型的指针变量之间是可以相互赋值的
-
分组导航标记
导航栏中的main.m后的No Selection点击选择方法或类
- #pragma mark 分组名
在@interface和@implementation上方添加
- #pragma mark -
在导航条对应的位置显示一条水平分割线
- #pragma mark - 分组名
在导航条对应的位置产生一条水平分割线,再产生分组名
-
函数和方法对比
- 在C中学习的叫做函数
void test(){}
- 在OC类当中写的叫方法
- 相同点:
都是用来封装代码,表示一个相对独立的功能
- 不同点:
a. 语法不同.
b. 定义的位置不一样
OC方法的声明只能写在@interface的大括弧外面,实现只能写在@implementation.
函数除了函数的内部和@interface大括弧之中,其他地方都可以写
c. 调用的方式不同:
函数可以直接调用, 方法必须要创建之后调用
d. 方法是属于类的, 函数是对立的
最容易犯的错
1). @interface与@implementation不能相互嵌套
2). 类必须要先声明
3). 类的声明和实现必须都有要
4). 类的声明必须要放在使用类之前,实现可以放在其后
5). 声明类的时候,声明和实现必须要同时存在.
特殊情况: 可以只有实现,没有声明. 但不可这么写
6). 属性名一定要以下划线开头, 类名首字母要大写
7). 属性不允许声明的时候赋值
8). OC方法必须要创建对象通过对象名来调用
9). 方法不能只声明不实现
unrecognized selector sent to instance 0x100420510
方法不存在或者没有实现