1、内存中的五大区域
栈:存储局部变量
堆:程序员手动申请的字节空间 malloc calloc realloc函数
BSS字段:存储未被初始化的全局变量、静态变量
数据段(常量区):存储已被初始化的全局变量、静态变量、常量数据
代码段:存储代码,存储程序的代码
2、类加载
1)在创建对象的时候,肯定是需要访问类的
2)声明一个类的指针变量也会访问类的
在程序运行期间,当某个类第一次被访问的时候,会将这个类存储到内存中的代码段区域,这个过程叫做类加载。
只有类在第一次被访问的时候,才会做类加载。
一旦类被加载到代码段以后,直到程序结束的时候才会被释放
3、对象在内存中究竟是如何存储的
假设下面这个写在函数之中
Person *p1 = [Person new];
1)Person *p1;会在栈内存中申请一块空间,在栈内存中声明一个Person类型的指针变量p1,
p1是一个指针变量,那么只能存储地址。
2)[Person new];真正在内存中创建对象的其实是这句代码。
new做的事情:
a、在堆内存中申请一块合适大小的空间;
b、在这个空间中根据类的模板创建对象;
类模板中定义了什么属性,就把这些属性依次声明在对象之中,
对象中还有另外一个属性,叫做isa,是一个指针,指向对象所属的类在代码段中的地址。
c、初始化对象的属性
如果属性的类型是基本数据类型,那么就赋值为0;
如果属性的类型是C语音的指针类型,那么就赋值为NULL;
如果属性的类型是OC的类指针类型,那么就赋值为nil;
d、返回对象的地址
3)注意
a、对象中只有属性,而没有方法,自己类的属性外加一个isa指针指向代码段中的类
b、如何访问对象的属性
指针名->属性名;
根据指针,找到指针指向的对象,再找到对象中的属性来访问
c、如何调用方法
[指针名 方法名];
先根据指针名找到对象,对象发现要调用方法,再根据对象的isa指针找到类,然后调用类里的方法
4、为什么不把方法存储在对象之中?
因为每一个对象的方法的代码实现都是一模一样的,没有必要为每一个对象都保存一个方法,这样的话就太浪费空间了,
既然都一样,那么就只保持一份。
5、对象的属性的默认值
如果我们创建一个对象,没有为对象的属性赋值,那么这个对象的属性是有值的。
如果属性的类型是基本数据类型,那么默认值值为0;
如果属性的类型是C语音的指针类型,那么默认值为NULL;
如果属性的类型是OC的类指针类型,那么默认值为nil;
1)NULL
可以作为指针变量的值,如果一个指针变量的值是NULL值代表,代表这个指针不指向内存中的任何一块空间,
NULL其实等价于0;NULL其实是一个宏,就是0.
2)nil
只能作为指针变量的值,代表这个指针变量不指向内存中的任何一块空间,
nil其实也等价于0,也是一个宏,就是0;
所以,NULL和nil其实是一样的;
3)使用建议
虽然使用NULL的地方可以是nil,使用nil的地方可以使用NULL,但是不建议大家去随便使用
C指针用NULL
int *p1 = NULL; //p1指针不指向内存中的任何一块空间;
OC的类指针用nil
Person *p1 = nil; //p1指针不指向任何对象;
4)如果一个类指针的值为nil,代表这个指针不指向任何对象
Person *p1 = nil;
那么这个时候,如果通过p1指针去访问p1指向的对象的属性,这个时候会运行报错;
那么这个时候,如果通过p1指针去调用对象的方法,运行不会报错,但是方法不会执行,没有任何反应
6、多个指针指向同一个对象
1)同类型的指针变量之间是可以相互赋值的
Person *p1 = [Person new];
Person *p2 = p1; //这是完全没有问题的,p1和p2的类型都是Person指针类型的;
代表将p1的值赋值给p2,而p1的值是对象的地址,所以就把对象的地址赋值给了p2,所以p1和p2指向了同一个对象;
无论通过p1还是p2去修改对象,修改的都是同一个对象;
2)目前为止,只要看到new,就说明新创建了一个对象。
7、分组导航标记
1)#pragma mark 分组名
就会在导航条对应的位置显示一个标题
2)#pragma mark -
就会在导航条对应的位置显示一条水平分割线
3)#pragma mark - 分组名
就会在导航条对应的位置现产生一条水平分割线,再显示标题