2、static的作用:
static 局部变量: 变量只内存分配一次,生命周期类似于全局变量,在下一次调用时维持上一次的值,作用域在函数内部;
static 全局变量:只可以被模块内所有函数访问,但不能被模块外其他函数访问;
static 函数:只可被模块内的其他函数调用;
类中 static 成员变量:属于整个类所拥有,对类的所有对象只有一份拷贝,即派生类与基类共用一个静态成员变量;
类中 static 成员函数: 属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
3、线程与进程
进程:系统中正在运行的程序,每个进程都是独立的,一个进程奔溃不会影响其他进程,一个进程至少有一个线程;
线程:是进程的一个任务的执行,多个线程共享资源,一个线程奔溃整个进程就是奔溃。
4、堆和栈的区别
管理方式:对于栈来讲,是由编译器自动管理的,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存泄露
栈:在windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloc函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放的,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,他的机制是很复杂的。
5、
键值,KVC,键值编码,间接访问对象的属性,通过字符串来指定。这里的字符串就是键,属性值就是值。
键路径,就是用点作为分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。通过键路径,可以指定对象中的任意深度的路径,使其指向相关对象的特定属性。
6、
目标是动作消息的接收者,动作时控件发送给目标的消息。
UIBarButtonItem *saveBtn = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(saveRecipe:)];
一个按钮控件的click事件的实现. 在这里, 按钮被按下以后会调用 target(也就是self)上的saveRecipe方法. 按照objC的习惯来说是当click事件发生以后,会给self对象发送一个message导致self对象上的saveRecipe方法的调用.
* Target - Action 主要用来在MVC设计模式中,V和C之间的进行通信。
* V只负责发送对应的action给target,并不关心target具体做了什么。
7、objc内存管理规则
1、对于通过调用带有alloc、copy、mutableCopy、new或者create一词的方法创建的任何对象及其内存,你都拥有所有权。你负责在之后的某个时刻向该对象发送release消息来释放资源。使用类似[[Foo alloc]init...]命令创建的对象需要释放。任何使用类似[foo copy]方法创建的对象需要释放。任何和CreateFoo()类似的调用所返回的对象也需要释放。
2、对于通过不带上述词的方法调用获得的对象,你都没有所有权。这些对象可以再当前执行栈中任意调用,离开当前栈之后,这些对象就不可用了。
说明:从技术上说,对象如果之前没有被保留, 那么在下一个运行循环后会被释放。不过从遵循内存管理规则的角度考虑,你可以假定对象出了作用域后,只要没有被保留,就会被释放。
通过其他方法调用的对象通常是“自动释放”对象,自动释放对象会在应用程序下次离开运行循环时被释放。
8、autorelease
作用就是延迟释放,给对象添加延迟释放的标记
自动释放对象 atuorelease,在出了作用域之后,会被自动添加到"最近创建的"自动释放池中,在自动释放池被销毁或者耗尽的时候,会向池中所有对象发送release消息,释放池中对象。
10、单件实例
保证一个类只有一个实例,提供给所有文件共享
11、动态绑定
在运行时确定调用的方法
12、Obj-C优缺点
类别
动态识别
动态绑定
与C/C++混编
缺点:不支持命名空间(用前缀来区分);不支持运算符重载;不支持多重继承;使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(比如内联函数等),性能低劣。
16、delegate是一对一的,并且receiver可以返回值给sender;
notification可以一对多,receiver无法返回值给sender,所以delegate用于sender希望接收到receiver的某个功能反馈,notification用于通知多个对象事件。
17、
KVC key-value-code 键值编码,是通过字符串的形式 间接访问 对象的属性;
KVO key-value-observer 键值监听,监听某个属性的变化。
18、ViewController的loadView,viewDidUnload,viewDidLoad什么时候调用?
loadView: 在Controller的view为nil时调用;
viewDidLoad:在view从nib文件初始化时调用;
viewDidUnload: 内存资源紧张的时候,释放view相关对象,
20、类别的作用
1)利用类别分散实现
将类的实现分散到多个不同文件或多个不同框架中;
2)创建私有方法的前向引用
Cocoa没有任何真正的私有方法,只要知道对象支持的某个方法的名称,即便在该对象的类的接口没有该方法的申明,你也可以调用该方法。不过,如果你这样使用,编译器会报警告,这个时候就可以用类别消除警告。也就是说接口没有该方法申明,则加上类别的该方法申明,就不会有警告;
3)向对象添加非正式协议
创建一个NSObject的类别称为:"创建一个非正式协议",因为NSObject是顶级父类,通过继承关系,所有的类中都有该方法。
类别的局限性:1)无法向类中添加实例变量;2)名称冲突时,类别具有更高的优先级,会取代原来的同名方法。
25、extension可以添加属性,Extension中的方法必须在@implementation中实现,否则编译会报错。
BLOCK
3、block的使用
使用typedef申明一个block
typedef void(^MyBlock)(NSString *);//申明了一个返回值为void,有一个输入参数NSString* ,名为MyBlock的block。
申明block对象
@property(nonatomic, copy) MyBlock myBlock;
注意对象属性一定要用copy,自动复制一份,因为block是存放在栈中,随时会被回收,需要copy操作。
默认,block内部不能修改外部变量,加__block关键字就可以修改外部变量了
3、关键字constant
constant 变量:变量是只读的,初始化后不能被修改;
constant 指针:指针本身不能改变,或是指针指向的内容不能被改变;
constant 函数的形参:表明在函数内部不能改变;
constant 类的成员方法:常函数,不能修改类的成员变量;有时候必须返回constant类型,使得返回值不为"左值"。
4、关键字volatile
总是从原始的地址读取这个变量,而不是从寄存器的备份中得到,这个变量可能被意想不到地改变。
并行设备的硬件寄存器(如:状态寄存器);
中断服务子程序中会访问都的非自动变量;
多线程应用中被几个任务共享的变量;
5、关键字static
static 局部变量;函数内部只初始化一次,下一次调用时会保持上一次的结果;
static 全局变量:能被模块内所有函数访问,不能被其他模块访问;
static 函数:只能被其他函数调用,不能被其他模块调用;
static 成员变量:属于整个类所拥有,对于类的所有对象只有一份拷贝,也就是说被类的所有对象共享;
static 成员方法:属于整个类所拥有,该函数不接收this指针,所以只能访问类的static成员变量。
7、线程和进程
进程是系统正在运行的程序,进程之间是相互独立的,一个进程的奔溃不影响其他进程,一个进程至少有一个线程;
线程是进程的一次任务执行,多个线程共享进程分配的内存空间,一个线程奔溃则进程就奔溃。
8、堆和栈的区别
管理方式:栈是由编译器自动管理,无需手动控制;
堆是由程序员手动管理,容易产生内存泄露。
申请大小:栈是向低地址扩展的数据结构,是一块连续的内存区域,能从栈获得的空间较小;
堆是向高地址扩展的数据结构,是个不连续的内存区域,能从堆获得的空间较大。
碎片问题:堆,频繁的new/delete会造成内存空间的不连续,出现大量的碎片,使程序效率降低;
栈,不会存在这个问题,因为栈是先进后出的队列,不可能有一块内存从栈中间弹出。
分配方式:堆,动态分配
栈,静态分配和动态分配:静态分配,如局部变量的分配,是由编译器完成的,动态分配是由 alloc函数分配,栈的动态分配是由编译器管理的,无需程序员手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,效率较高;
堆是C/C++函数库提供的,机制复杂,效率较低。
9、内存管理
有一个原则:谁创建谁释放
1)你使用alloc new copy方法创建对象,若是不用需负责release/autorelease;
2)你用其他方法得到一个对象,则不需要执行任何操作来确保该对象被清理;
3)你保留了一个对象,需要释放该对象,必须保持retain和release方法的使用次数相等。
10、循环引用
对象a创建并引用到了对象b;
对象b创建并引用到了对象c;
对象c创建并引用到了对象b;
这时b的引用计数是2,c的引用计数是1,当a不再使用b,调用release释放对b的所有权,c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。b和c永远保留在内存中。
delegate使用assign(或是weak)就是为了打断循环引用。如果一个UITableViewController拥有一个UITableView,而这个UITableView的delegate对象又是该控制器,若是用retain就会陷入循环引用,没有机会释放这两个对象。
11、
copy在setter方法中先release旧指,再copy新值,有一份新的拷贝,比如block,因为block是保存在栈中,随时可能被回收,所以需要copy
assign 在setter方法中,只是进行赋值,用在基础数据类型int NSInteger;
retain 在setter方法中先release旧指,再retain新值
12、runLoop
UI事件,"接受消息->等待->处理" 的循环
14、tableView的重用机制
重用机制 简单的说 意思 一行一行 的cell 都是在复用的, 滑动 tableview 的时候,刚离开视图的 cell 会被放到复用池 中,等下一个 cell需要 显示时,会先看复用池中有没有 cell 如果有的时候 ,就从复用池中拿出来cell ,没有的话就重新创建cell 。
visibleCells数组,reusableTableCells数组
tableView总是先从缓存池中找带重用标示符的
比如,有100条数据,屏幕上最多显示10条
1>先用[UITableView alloc]initWithStyle reuseIdentifier:CellIdentifier];
创建10次cell,并给cell执行重用标示符CellIdentifier,那么这10个cell加入到visibleCells数组里,reusableTableCells数组里为空;
2>拖动tableView,但cell1完全移除屏幕时,cell11完全显示(它也是alloc出来的),cell11加入到visibleCells数组里,cell1加入到reusableTableCells数组里;
3>因为reusableTableCells中已经有值了,所以当需要显示新的cell,[ tableView dequeueReusableCellWithIdentifier: CellIdentifier],返回cell1,cell1加入到visibleCells数组,cell1移出reusableTableCells;cell2移出visibleCells数组,cell2加入到reusableTableCells数组。之后再需要显示的cell就可以正常重用了。
18、Model,Controller,View相互通讯的规则:
- Controller可以直接和Model通信
- Controller也可以直接和View通信
- Model和View永远不能直接通信
- iOS中View和Controller的通信是透明和固定的,主要通过outlet和action实现
- View使用Delegate接口和Controller同步信息
- View不直接和数据通信,使用dataSource接口从Controller处获取数据
- View的delegate和dataSource一般就是Controller
- Controller负责为View翻译和格式化Model的数据
- Model使用Notification & KVO的方式分发数据更新信息,Controller可以有选择的监听自己感兴趣的信息。
- View也可以监听广播信息,但一般不是Model发出的信息
- 一个完整的App就是很多MVC的集合
iOS开发设计模式;
(一)代理模式:
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
列表row个数delegate
自定义的delegate
(
二)观察者模式
应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。
优势:解耦合
敏捷原则:接口隔离原则,开放-封闭原则
实例:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
kvo,键值对改变通知的观察者,平时基本没用过。
(三)MVC模式
应用场景:是一中非常古老的设计模式,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优势:使系统,层次清晰,职责分明,易于维护
敏捷原则:对扩展开放-对修改封闭
实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。
(四)单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:使用简单,延时求值,易于跨模块
敏捷原则:单一职责原则
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,
返回的也只是此单例类的唯一静态变量。
装箱与拆箱 由于NSArray,NSDirectory等类不能直接存储基本数据类型,所以要想在NSArray\
NSDirectory中使用基本数据类型,就得使用装箱与拆箱。
在Objective-C中,可以使用NSNumber和NSValue来实现对数据类型的包装, NSNumber可以实现对基本数据类型的包装,NSValue可以实现对任意类型数据的包 装。
将基本类型封装成对象叫装箱,从封装的对象中提取基本类型叫拆箱(取消装箱),其它 语言如Java原生支持装箱与拆箱,Ojbective-C不支持自动装箱与拆箱,如果需要得需要 自己来实现装箱与拆箱。