小鱼儿不才,总结了些易混淆的点,如有错误请联系我或者评论,小鱼儿及时更正QQ:593216709
堆与栈的区别
栈 :是由系统自动分配释放的一块存储空间,存放函数的参数值,、局部变量等(数据类型,以及定义的变量和对象的指针),类似于数据结构中的
堆:一般由程序员自己手动释放,存放OC对象,程序员不手动释放,会到最够关闭程序时对象才会被释放掉,分配方式类似于链表。
instancetype和id的区别
instancetype :返回的指针指向的类型是已知的,不可以当方法的参数使用,只能作为返回值。
id:返回的指针指向的类型是未知,可以当做方法的参数使用,也是可以作为返回值
MRC与ARC的区别
ARC:自动管理内存计数器。不需要程序员管理内存,编译器会在恰当的时候给我们填上reatin、release等代码。
注意:oc中的ARC和JAVa里面的垃圾回收机制不太一样,Java中的垃圾回收机制是系统帮忙干的,而oc中的ARC是编译器干的。
- ARC是编译器特性(编译器会在恰当的时候给我们填上reatin、release等代码),不是运行时特性。
MRC:手动管理内存计数器。需要程序要自己恰当的地方自己写上release和retain代码。
野指针和空指针的区别
野指针:表示指针指向了一个被释放的对象,也成“僵尸对象”。只要给野指针发送消息,程序就会报错。
空指针:指向没有储存控件的指针(就是指针设置为nil)。我们一般为了避免给野指针发送消息,当一个对象被释放后我们都将这个对象的指针设置为空指针。给空指针发送消息不会报错
property修饰符
readonly:只读的,修饰的属性只自动生成getter方法
readwrite :可读可写的,修饰的属性可以自动生成getter/setter方法。默认情况下是readwrite
retain:自动帮我们生成getter/setter方法,并且生成的是内存管理代码(引用计数器+1)。
assign:不会自动帮助我们在setter/getter方法中生成内存管理代码,仅仅生成普通的getter与setter方法
getter:可以给getter方法起一个名字
setter:可以给setter方法起一个名字
atomic:原子性,性能比较低,安全性高。(只能执行一条线程)
nonatomic:非原子性,性能比较高,安全性比较差 (可以同时进行多条线程)
+(void)load和+(void)initialize的区别
+(void)load:表示程序开始加载到内存时调用 2.自身未定义,不会沿用父类的方法。3.lei别中的定义全部执行,先执行类自身的实现,再执行类别(Category)中的实现
+(void)initialize:便是该类第一次使用将被调用 2.自身未定义,hui沿用父类的方法。3.类别中的定义,会执行最后一个,并且覆盖类的方法
http://www.cnblogs.com/ider/archive/2012/09/29/objective_c_load_vs_initialize.html
weak和strong assign
strong:强引用,用于OC对象,相当于MRC中的reatin,引用计数器加1
weak:弱引用,用于OC对象,相当于MRC中的assign,引用计数器不加1
assign:用于一般数据类型,和MRC中的assign一样,引用计数器不加1
category的使用
category的声明:
@interface Person (WJPerson)
-(void)goShopping;
@end
category的实现
@implementation Person (WJPerson)
-(void)goShopping
{
NSLog(@"去购物");
}
@end
Person 是对这个类进行分类
WJPerson 是分类的名字
category的使用:
1.category可以在不改变原有类的基础上,给类扩充一些方法
2.可以让庞大的类分模块开发。
3.一个庞大的类进行多人开发,更利于团队合作。
category的注意点:
1.分类不能扩充属性和成员变量,只能给类扩充方法
2.分类里面的@property只能生成setter和getter的声明,并不能生成setter和getter的实现
3.如果分类里面有和类里面相同名字的方法,对象调用的时候回调用分类的方法,不去调用类里面的方法。如果有多个分类里面同名的方法,则会调用最后一个参与编译分类里面的方法
block的存储地址
1.block可以存储在堆中也是可以存储在栈中的。默认是存储在栈中,如果对block经营一次copy操作,block就会转移到堆中存储。
2.如果block在栈中存储时,block访问了外部的对象,那么不会对对象进行retain操作。
3.如果block在堆中存储,block访问了外部对象,那么会对外部对象进行一次retain操作。
4.只要给外部的对象前面加上__block,不管block是堆中还是栈中,都不会对对象进行retain操作
protocol(协议)和继承的区别
protocol 就是保存一些方法的声明,没有方法的实现。只要遵循了这个协议,就拥有了协议声明的方法
1。继承是可以继承父类的方法和实现的。而遵守protocol后,只拥有了协议里面的声明的方法,但没有协议声明方法的实现
2.相同的类型的类可以使用继承,但是不同的类只能使用protocol。
3.protocol可以用于存储方法的声明,可以将多个类中共同的方法抽取出来,以后让这些类遵守协议即可,(遵守了协议的类,就拥有了协议声明的方法)
protocol的关键字@required @optional的区别
@required
// 如果声明的方法使用required的修饰,说明此方法是必须实现的,如果遵循的类,没有实现,则会有警告,并且默认声明的方法否是required修饰
-(void)palyBaseBall;
@optional
//如果声明的方法使用optional修饰,说明此方法可以不实现,遵循协议的类及时不实现此方法,也不会报警告
-(void)palyBasketBall;
#import和#include的区别,@class代表什么?
import和#include指令都是用于包含头文件的,前者是保证只会包含一次,不会重复包含;后者是c语言中原来就有的包含头文件的指令,在objc开发中,若是c文件,一件会使用#include指令来包含头文件,为了防止重复包含,通常会加上条件编译
@class 就是告诉编译器,后面的是一个类。但是无法知道类里面的方法和属性。很好的解决了引用循环的问题。
nil NULL 和Nil的区别
nil 针对对象,指向OC中对象的空指针。
Nil 针对类,指向OC中类的空指针。
NULL 针对其他数据类型,指向其他类型的空指针。如基本数据类型为空。
#define定义的宏和const定义的常量有什么区别?
- define定义宏的指令,程序在预处理阶段将用#define所定义的内容只是进行了替换。因此程序运行时,常量表中并没有用#define所定义的宏,系统并不为它分配内存,而且在编译时不会检查数据类型,出错的概率要大一些
- const定义的常量,在程序运行时是存放在常量表中,系统会为它分配内存,而且在编译时会进行类型检查
常见的出现内存循环引用的场景有哪些
- 定时器(NSTimer):NSTimer经常会被作为某个类的成员变量,而NSTimer初始化时要指定self为target,容易造成循环引用(self->timer->self)。 另外,若timer一直处于validate的状态,则其引用计数将始终大于0,因此在不再使用定时器以后,应该先调用invalidate方法
- block的使用:block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题, 一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.someBlock = Type var{[self dosomething];或者self.otherVar = XXX;或者_otherVar = …};出现循环的原因是:self->block->self或者self->block->_ivar(成员变量)
- 代理(delegate):在委托问题上出现循环引用问题已经是老生常谈了,规避该问题的杀手锏也是简单到哭,一字诀:声明delegate时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong,毕竟这基本逃不掉循环引用了!