OC 知识复习 (二) 类与对象

20240201215148_image.png

1.内存中的五大区域,

	A.栈    存储局部变量,
	B.堆    程序员手动申请的字节空间 malloc calloc realloc函数,
	C.BSS段 存储未被初始化的全局变量 静态变量,
	D.数据段(常量区) 存储已被初始化的全局 静态变量 常量数据,
	E.代码段 存储代码。
copy success

2.类加载,

1)。在创建对象的时候 肯定是需要访问类的
2),声明1个类的指针变量也会访问类的

在程序运行期间 当某个类第1次被访问到的时候。
会将这个类存储到内存中的代码段区域,这个过程叫做类加载,

只有类在第1次被访问的时候,才会做类加载一旦类被加载到代码段以后,
直到程序结束的时候才会被释放,
copy success

3.对象在内存中究竟是如何存储的,

假设下面这个写在函数之中.

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指针找到类然后调用类里的方法,
					
	4. 为什么不把方法存储在对象之中?
	
			因为每1个对象的方法的代码实现都是一模一样的 
			没有必要为每1个对象都保存1个方法 
			这样的话就太浪费空间了既然都一样 那么就只保持1份。
			
	5.对象的属性的默认值,
	
		如果我们创建1个对象,没有为对象的属性赋值,那么这个对象的属性是有值的
		
		如果属性的类型是基本数据类型 默认值是0
		如果属性的类型是C指针类型 那么默认值是NULL
		如果属性的类型是OC指针类型,那么默认值是nil
copy success

  1. NULL

     只能作为指针变量的值,如果1个指针变量的值是NULL值代表。
     代表这个指针不指向内存中的任何1块空间NULL其实等价于0
     NULL其实是1个宏. 就是0
    
    copy success
  2. nil

     只能作为指针变量的值,代表这个指针变量不指向内存中的任何空间,
     nil其实也等价于0 也是1个宏.就是0.
    
    copy success

所以,NULL和nil其实是一样的

3.使用建议。

虽然使用NULL的地方可以是nil 使用 nil的地方可以使用NULL 但是不建议大家去随便使用。

C指针用 NULL
	int *p1 = NULL;  p1指针不指向内存中的任何1块空间,

OC的类指针用 nil
	Person *p1 = nil; p1指针不指向任何对象,
copy success
  1. 如果1个类指针的值为nil 代表这个指针不指向任何对象,

     Person *p1 = nil;
     
     那么这个时候 如果通过p1指针去访问p1指针指向的对象的属性 这个时候会运行报错。
     那么这个时候,如果通过p1指针去调用对象的方法 运行不会报错 但是方法不会执行 没有任何反应
    
    copy success

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

Person *p1=[Person new];
Person *p2 = p1;这是完全没有问题的。p1和p2的类型都是Person指针类型的
代表将p1的值赋值给p2 而p1的值是对象的地址 所以就把对象的地址赋值给了p2
所以p1和p2指向了同1个对象,
无论通过p1还是p2去修改对象,修改的都是同1个对象,
copy success

分组导航标记:

1). #pragma mark 分组名

	 就会在导航条对应的位置显示1个标题,
	 
2).#pragma mark -
	
	就会在导航条对应的位置显示1条水平分隔线,

3).#pragma mark - 分组名

	就会在导航条对应的位置先产生1条水平分割线,再显示标题,
copy success

1.最容易犯错

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

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

3). 类的声明和实现必须都要有 就栓没有方法 类的实现也不必不可少的,

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

5). 声明类的时候 类的声明和实现必须要同时存在,
		特殊情况下可以只有实现 没有声明,

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

7). 属性不允许声明的时候初始化
	  在为类写1个属性的时候 不允许在声明属性的时候为属性赋值。
	
8). OC方法必须要创建对象通过对象名来调用

9). 方法只有声明 没有实现   
 
  a. 如果方法只有声明 没有实现编译器会给1个警告不会报错
	b. 如果指针指向的对象 有方法的声明 而没有方法的实现 那么这个时候通过指针来调用这个方法在运行的时候就会报错
	unreconized selector sent to instance 0x100420510
	只要你看到了这个错误,说明要么对象中根本就没有这个方法,要么只有方法的声明而没有方法的实现
copy success

多文件开发

1.所有的类都写在main.m这个源文件之中的。后果:后期的维护就非常的不方便,也不利于团队开发

2.推荐的方式

	把1个类写在1个模块之中,而1个模块至少包含两个文件
	
	.h 头文件
		
			写的类声明 因为要用到Foundation框架中的类 NSObiect 所以在这个头文件中要引入Foundation框架的头文件然后将类的声明的部分写在,h文件中
		
	.m 实现文件
			先引入模块的头文件 这样才会有类的声明再写上类的实现,
		
		如果要用到类,只需要引入这个了模块的头文件就可以直接使用了

3. 添加类模块的更简洁的方式

		NewFile Cocoa Class 自动生成模块文件 .h .m 自动的将类的声明和实现写好,
		填写的名称是决定模块文件的名称,
		类名是可以自己再改的但是建议模块的文件名和模块中的类名保持一致,这样方便代码的管理

4. 当我们要使用这个类的时候,需要先将这个类的头文件先引进来,才可以使用。
copy success