最近在通过小码哥的iOS开发教学视频重新回顾复习OC的知识点。这篇文章用来记录一下学习中的重点和心得,便于以后再进行回顾,也可以让其他初学者一起学习交流。
一、Objective-C中的类与对象
1.类方法中不可以访问属性变量(因为还没有创建类的对象实例,还不存在属性变量的实例);
2.在对象方法中可以直接调用类方法。但是在类方法中不可直接调用对象方法(虽然可以间接调用,但这样做没有实际意义,不建议这样做)。
3.当一个方法不需要使用类中的属性变量时,能使用类方法最好使用类方法。因为类方法的执行效率比对象方法更高:
调用对象方法时,需要先通过对象的指针访问内存中储存对象数据的地址,并找到isa指针,再通过isa指针找到类对象中所对应的目标方法;而调用类方法时不需要中间的两部过程,而是直接访问类对象中的目标方法。因此类方法的执行效率明显高于对象方法。
4.通过类创建对象时,返回的地址就是类的第0个属性的地址。但是!但是类的第0个属性不是手动创建的属性,而是一个叫做isa的属性。(isa是一个指针,指向这个实例对象所属的类)
5.OC中点语法的本质是编译器特性,编译器自动将点语法转换为getter或setter方法
6.关于self的用法:
在对象方法中,self代表调用当前对象方法的那个实例对象;在类方法中,self代表调用类方法的类。由编译器自动区分
注意:不能在方法中通过self再次调用该方法,因为这会导致无限调用死循环。
7.继承时,方法可以重写,但属性(成员变量)不能重写。
8.关于super的用法:
在对象方法中,调用父类的对象方法;在类方法中,调用父类的类方法。且super可以在任意方法中调用父类的方法。
super一般用在重写方法时,用来保留父类方法中的功能。
9.什么是多态?
用父类的指针指向一个子类的对象,这就是多态。其在编译时表现为父类类型,在运行时表现为真实类型。
10.如果一个方法只有实现而没有声明,那么这个方法就是所谓的"私有方法"。(Objective-C中没有真正的私有方法)
11.id和instancetype
1)id和instancetype都是动态类型,但id在编译时不知道其真实类型,而instancetype在编译时编译器就可以判断其真实类型。
2)instancetype只能用于作为返回值,不能用来定义变量或作为形参;id则均可。
3)自定义构造方法和工厂方法时,返回值应该尽量使用instancetype而非id,以确保编译器可以在编译时检查类型错误。
4)id的应用场景:多态。可以减少代码量-避免调用子类特有的方法时进行强制类型转换(但是要注意类型检查,从而避免运行时错误)
12.工厂方法
0)工厂方法是用来快速创建实例对象的类方法;
1)一定是类方法;
2)方法名称以类名开头,首字母小写;
3)一定有返回值,且返回值类型必须是instancetype或id
4)工厂方法最好与对象构造方法一一对应。
ex:需要注意的是,在工厂方法中,创建对象的语句不要用类名来创建(即类似于[[Something alloc] init]的形式),而是应该使用self来创建(类似于[[self alloc] init]的形式)。这样可以避免子类调用父类的工厂方法时创建了一个父类的对象,导致运行时错误。
13.类的本质
1)类本质上也是一个对象。类对象在第一次被使用时创建;
2)类对象中储存着类的属性和对象方法。类对象中有一个isa指针,指向一个元类对象;
3)元类对象中储存着类的类方法。元类对象中有一个isa指针,指向一个根元类对象(也就是NSObject的元类对象);
4)根元类对象中储存着类的new方法。根元类对象中有一个指向自身的isa指针。
14.类的启动过程
1)当程序启动时,所有类的代码会被加载到内存中的代码区,并且自动调用每个类的+load方法。如果需要对程序启动时进行一些一次性的配置,可以重写类的该方法。
2)当一个类首次被使用时,会调用一次+initialize方法,并且在整个程序运行期间只会调用一次。可以重写这个方法来对某一个类进行一次性的初始化。
3)如果存在继承关系,会先调用父类的+load和+initialize方法,再调用子类的方法。
15.SEL类型
SEL类型通过@seletor方式接受一个方法名创建,有以下3个左右:
1)通过-responsToSeletor:方法来检查对象/类是否实现了某个方法;
2)通过-performSelecor:可以配合对象/类来调用一个方法;(如果需要传参数,可以增加withObject:来实现,但是最多只能传2个参数)
3)配合对象将SEL类型的对象作为一个方法的参数来传递,从而实现方法。
16.文件编写规范
在.h中尽量使用@class来声明会用到的类,而不是用#import导入类文件;而在.m文件中才用#import导入类的头文件来获取声明。可以极大的提高编译的效率。(因为可以避免文件的改动导致的大量连锁重新导入)
二、Objective-C的内存管理
1.MRC和ARC
MRC(Manul Reference Counting),即手动引用计数。程序中创建的对象都需要程序员自行管理,手动释放。
MRC中内存管理的机制:通过引用计数器确定对象释放应当释放内存。对象创建时引用计数为1,仅当引用计数为0时,系统自动释放。
ARC(Automatic Reference Counting),即自动引用计数。是Apple在2011年iOS5时引入一种自动管理内存的机制,可以自动帮助程序员管理程序中创建的对象,在恰当的时机自动释放。
ARC中内存管理的机制:通过程序中指向对象的强指针数量来确定对象是否应当释放。只要有强指针指向对象所在的内存空间,则不会释放内存。当没有强指针指向对象时,对象被销毁,内存空间被释放。
注意:ARC本质上是一种编译器特性,是有编译器在代码中适当的地方自动帮程序员添加retain和release代码。与Java中系统定时进行垃圾清理的机制有本质区别。
2.野指针
在MRC中,当一个对象被释放后,指向这块内存空间的指针就变成了野指针。当给一个野指针发送消息时,会导致运行时错误(因为已经被释放的对象中没有方法)。通常可以在对象被释放后将原本指向其内存空间的指针置为空指针来避免该问题。因为OC中对空指针发送消息不会有任何问题,也不会做任何事情。
3.内存管理准则
MRC中的内存管理遵循:谁创建谁释放、谁retain谁release、谁copy谁release。也就是说只要有一次使对象的引用计数+1的行为,就必须对应一次release即可。
4.自动释放池
@autoreleasepool的机制是当pool被销毁时,自动向池中的每一个对象发送一条release消息。对对象调用autorelease方法可以将对象加入到栈顶的自动释放池中。
由于自动释放池本质上是延迟释放,因此最好不要在池中保存占用内存很大的对象,也不要在池中放入次数很大的循环操作,避免在池销毁前占用的内存过大。
5.弱指针
__weak关键字修饰的指针是弱指针。弱指针指向的对象的释放不受弱指针的影响。即:即使还有弱指针指向对象,只要没有强指针指向了对象,对象还是会被自动释放。
但弱指针所指的对象如果被释放了,弱指针会被自动置为nil。合理的使用弱指针可以有效的避免野指针的情况。
三、Category(分类、类别)
Category是OC特有的语法,是对类的扩展,用于扩充已有类的方法。
1.Category的优势
1)可以在不修改原有类的基础上,扩充类的方法
2)可以把一个庞大的类分模块来开发,减少一份文件中的代码量,提高代码的结构性和可读性。
3)可以便于多人开发,方便多人同时开发一个类。
2.注意点
1)只能添加方法,不能添加属性(成员变量)
2)如果在分类中使用@property,只会生成setter和getter方法的声明,不会生成实现代码,也不会添加成员变量。
3)如果分类中有和原有类中同名的方法,会调用分类中的方法,忽略原有类中的方法。如果多个分类中有同名的方法,则调用时调用的是最后编译的那个分类中的方法。(主要不要这样写!这样写代码的可读性和可维护性极低!)
四、Block
Block是一种数据类型,可以保存一段代码,在恰当的时候取出来调用。
1.注意点
1)block可以访问外部变量,用到的变量是一份拷贝(值传递)
2)block可以定义和外部同名的变量,会覆盖外部的定义。
3)默认情况下不可以在block中修改外部变量的值,如果需要修改,则需要将给外部变量加上__block关键字。(传递地址)
4)block默认在栈中,对block进行copy后会转移到堆中
五、@property的修饰符
@property共有以下关键字:
1)readonly/readwrite:分别代表只读或可读写。用readonly修饰的属性只会生成getter方法。用readwrite修饰的属性则getter方法和setter方法都会生成
2)atomic/nonatomic:分别代表线程安全和非线程安全。 用atomic修饰的属性在多线程编程中是线程安全的,但执行效率低。用nonatomic修饰的属性不安全,但执行效率高。在iOS开发中通常使用nonatomic。
3)retain/assign:在MRC中使用。assign表示仅赋值,不自动添加retain、release代码,通常对基本数据类型的属性使用。retain表示让编译器自动添加retain、release代码,通常对对象属性使用。
4)strong/weak/assign:在ARC中使用。strong表示用强指针指向该成员变量,大部分对象类型用该修饰符。weak表示弱指针指向成员变量,以避免循环引用的情况,通常用在UI中的子控件。assign与MRC中相同,通常用于基本数据类型。
5)copy:表示该属性将通过copy的方式创建。常用于NSString、NSArray、NSDictionary这些拥有可变版本的类,用来避免外部变量的修改导致成员变量的改变。
六、protocol
protocol(协议)是储存方法的声明,用来表示某个类型的对象需要实现某些方法。协议仅仅用于程序员之间的交流,只会引起编译器警告,不会报错。
1.应用场景:
1)将协议写在数据类型右边,作为类型限定,表明赋值时,对象必须遵守了该协议。
2)在代理设计模式中使用。
2.注意点:
1)只能声明方法,不能声明属性;
2)父类遵守了某协议,则子类也遵守该协议;
3)一个类可以遵守多个协议;
4)协议也可以继承其他协议,书写方式与类继承类似。
3.协议编写规范:
1)一般情况下,协议属于谁,就将协议定义到谁的头文件中;
2)协议名称一般以类名开头,跟上protocol或delegate;
3)协议中的方法一般以类名为前缀;
4)一般情况下,类中代理属性的名称都是delegate;
5)在.h文件中用@protocol+协议名称来声明协议,在.m文件中再用#import导入协议的.h文件。可以提高编译效率