Objc
一.三大特性
1.封装_点语法
1> 本质
//以下代码有什么问题 - (void)setName:(NSString *)name { self.name = name; } - (NSString *)name { return self.name; }
点语法的本质是调用类的getter方法和setter方法,如果类中没有getter方法和setter方法就不能使用点语法。
2.继承
1> 如何实现多重继承
消息转发 forwardingTargetForSelector methodSignatureForSelector forwardInvocation
delegate和protocol
类别
3.多态
1> 什么是多态
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。
由于每个类都属于该类的名字空间,这使得多态称为可能。类定义中的名字和类定义外的名字并不会冲突。类的实例变量和类方法有如下特点:
- 和C语言中结构体中的数据成员一样,类的实例变量也位于该类独有的名字空间。
- 类方法也同样位于该类独有的名字空间。与C语言中的方法名不同,类的方法名并不是一个全局符号。一个类中的方法名不会和其他类中同样的方法名冲突。两个完全不同的类可以实现同一个方法。
方法名是对象接口的一部分。对象收到的消息的名字就是调用的方法的名字。因为不同的对象可以有同名的方法,所以对象必须能理解消息的含义。同样的消息发给不同的对象,导致的操作并不相同。
多态的主要好处就是简化了编程接口。它容许在类和类之间重用一些习惯性的命名,而不用为每一个新加的函数命名一个新名字。这样,编程接口就是一些抽象的行为的集合,从而和实现接口的类区分开来。
Objective-C支持方法名的多态,但不支持参数和操作符的多态。
2> OC中如何实现多态
在Objective-C中是通过一个叫做selector的选取器实现的。在Objective-C中,selector有两个意思, 当用在给对象的源码消息时,用来指方法的名字。它也指那个在源码编译后代替方法名的唯一的标识符。 编译后的选择器的类型是SEL有同样名字的方法、也有同样的选择器。你可以使用选择器来调用一个对象的方法。
选取器有以下特点:
* 所有同名的方法拥有同样的选取器
* 所有的选取器都是不一样的
(1) SEL和@selector
选择器的类型是 SEL。@selector指示符用来引用选择器,返回类型是SEL。
例如:
SEL responseSEL;
responseSEL = @selector(loadDataForTableView:);
可以通过字符串来得到选取器,例如:
responseSEL = NSSelectorFromString(@"loadDataForTableView:");
也可以通过反向转换,得到方法名,例如:
NSString *methodName = NSStringFromSelector(responseSEL);
(2) 方法和选取器
选取器确定的是方法名,而不是方法实现。这是多态性和动态绑定的基础,它使得向不同类对象发送相同的消息成为现实;否则,发送 消息和标准C中调用方法就没有区别,也就不可能支持多态性和动态绑定。
另外,同一个类的同名类方法和实例方法拥有相同的选取器。
(3) 方法返回值和参数类型
消息机制通过选取器找到方法的返回值类型和参数类型,因此,动态绑定(例:向id定义的对象发送消息)需要同名方法的实现拥有相 同返回值类型和相同的参数类型;否则,运行时可能出现找不到对应方法的错误。
有一个例外,虽然同名类方法和实例方法拥有相同的选取器,但是它们可以有不同的参数类型和返回值类型。
3> 动态绑定
二.类和对象
1.category
1> 分类 拓展 协议中哪些可以声明属性?
都可以,但分类和协议创建的属性只相当于方法,但是内部没有对成员变量的操作(无法创建成员变量),拓展可以(私有成员变量)
代理中声明属性,没有实际创建成员变量,相当于声明了属性名对应的访问方法,遵守协议的类需要实现对应的访问器方法,否则运行报错
分类中声明属性,警告提示需要手动实现访问器方法(Swift中叫计算型属性),而分类中不能创建成员变量,可以在手写访问器方法中使用runtime的 objc_setAssociatedObject方法关联对象间接创建属性(静态库添加属性)
拓展里可以声明属性,直接可以使用
2> 继承和类别的区别
1> 使用继承:
1.1> 添加新方法和父类方法一致,但父类方法仍需要使用
1.2> 添加新属性
2> 类别:
2.1> 针对系统提供的一些类,系统本身不提倡继承,因为这些类的内部实现对继承有所限制(NSString initWithFormat继承崩溃)
2.2> 类别可以将自己构建的类中的方法进行分组,对于大型的类,提高可维护性
3> 分类的作用
将类的实现分散到多个不同文件或多个不同框架中。
创建对私有方法的前向引用。
向对象添加非正式协议。
(非正式协议:即NSObject的分类,声明方法可以不实现,OC2.0以前protocal没有@optional,主要使用分类添加可选协议方法
oc中声明方法不实现,不调用则只警告不报错
正式协议的优点:可继承,泛型约束
如kvo的
observeValueForKeyPath属于nsobject的分类,且不需要调父类,说明可选实现该方法,没警告可能是编译器规则过滤
)4> 分类的局限性
无法向类中添加新的实例变量,类别没有位置容纳实例变量。
名称冲突,即当类别中的方法与原始类方法名称冲突时,类别具有更高的优先级。类别方法将完全取代初始方法从而无法再使用初始方法。
无法添加实例变量的局限可以使用字典对象解决。
2.extension
3.protocol
三.Foundation
1.字符串
1> 字符串比较
NSString *a = @“hello”; NSString *b = [NSString stringWithFormat:@hello”]; if (a == b){ nslog(@“a==b”); } if ([a isEqualToString: b]){ nslog(@“a isEqualToString b”); }
== 比较变量中保存的数值(地址) 速度快 内容同,可能地址不同(常量区,堆区)
isEqualTo 比较字符串 非常耗时
2> 字符串截取
截取字符串”20 | http://www.baidu.com”中,”|”字符前面和后面的数据,分别输出它们。
NSString * str = @"20 | http://www.baidu.com"; NSArray *array = [str componentsSeparatedByString:@"|"]; //这是分别输出的截取后的字符串 for (int i = 0; i<[array count]; ++i) { NSLog(@"%d=%@",i,[array objectAtIndex:i]); }
3> 格式
NSString *str1 = [NSString stringWithFormat:@"a"b”]; //报错,a”后加b非法 NSString *str2 = [NSString stringWithFormat:@“a""b”]; //显示 ab NSString *str3 = [NSString stringWithFormat:@“a"b”]; //显示 a”b 反斜杠转义
2.NSArray和NSDictionary
1> iOS遍历数组/字典的方法
数组: for循环 for in enumerateObjectsUsingBlock(正序) enumerateObjectsWithOptions:usingBlock:(多一个遍历选项,不保证顺序)
字典:
1. for(NSString *object in [testDic allValues]) 2. for(id akey in [testDic allKeys]){ [sum appendString:[testDic objectForKey:akey]]; } 3. [testDic enumerateKeysAndObjectsUsingBlock:^(idkey,idobj,BOOL*stop) { [sum appendString:obj]; } ];
速度: 对于数组, 增强for最快,普通for和block速度差不多,增强最快是因为增强for语法会对容器里的元素的内存地址建立缓冲,遍历的时候直接从缓冲中取元素地址而不是通过调用方法来获取,所以效率高.这也是使用增强for时不能在循环体中修改容器元素的原因之一(可以在循环体中添加标记,在循环体外修改元素)
对于字典,allValues最快,allKey和block差不多,原因是allKey需要做objcetForKey的方法
3.NSValue NSNumber
1> 归档视图尺寸,坐标
4.其他
nil Nil null NSNull 的区别
四.关键字
1.@property
1>readwrite,readonly,assign,retain,copy,nonatomic属性的作用
@property是一个属性访问声明,扩号内支持以下几个属性:
1.1> getter setter
getter=getterName,setter=setterName,设置setter与getter的方法名
1.2> weak assign strong copy
assign 用于非指针变量。用于基础数据类型 (例如NSInteger)和C数据类型(int, float, double, char, 等),另外还有id,其setter方法直接赋值,不进行任何retain操作
weak 用于指针变量,比assign多了一个功能,当对象消失后自动把指针变成nil,由于消息发送给空对象表示无操作,这样有效的避免了崩溃(野指针),为了解决原类型与循环引用问题
strong 用于指针变量,setter方法对参数进行release旧值再retain新值
copy 用于指针变量,setter方法进行copy操作,与retain处理流程一样,先旧值release,再copy出新的对象,retainCount为1。这是为了减少对上下文的依赖而引入的机制。copy是在你不希望a和b共享一块内存时会使用到。a和b各自有自己的内存。
1.3> readwrite,readonly,设置可供访问级别
1.4> nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。所以约定俗成只在主线程更新UI,防止多线程设置UI属性,出现资源抢夺现象
2> 如何避免循环引用
两个对象相互强引用,都无法release,解决办法为一个使用strong,一个使用assign(weak)
3> delegate的属性为什么使用assign/weak
避免出现循环引用,场景如UITableViewController强引用视图UITableView,而该视图的代理又是控制器,为避免循环引用,让delegate为弱引用
2.copy
1> copy的使用场景
当多个指针指向同一个对象时,为避免一个指针对对象的改动对其他指针的使用产生影响,使用copy来创建对象的副本
如页面间传值使用copy,A向B控制器传属性(属性为自定义对象),为避免因A的属性变化对B的属性产生影响
再如多人开发或封装库,在不明确传入值为可变还是不可变的情况下,使用copy更安全
2> 什么是深拷贝浅拷贝
对于非容器类对象,不可变对象进行copy操作为浅拷贝,引用计数器加1,其他三种为深拷贝
对于容器类对象,基本和非容器类对象一致,但注意其深拷贝是对象本身是对象复制,其中元素仍为指针复制,系统将initWithArray方法归为了元素深拷贝,但其实如果元素为不可变元素,仍为指针复制,使用归解档可以实现真正的深拷贝,元素也是对象拷贝NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array]];
3> 字符串什么时候使用copy,strong
属性引用的对象由两种情况,可变和不可变字符串
引用对象不可变情况下,copy和strong一样,copy为浅拷贝
引用对象可变情况下,如果希望属性跟随引用对象变化,使用strong,希望不跟随变化使用copy
4> 字符串所在内存区域
@“abc” 常量区 stringwithformat 堆区
5> mutablecopy和copy @property(copy) NSMutableArray *arr;这样写有什么问题
mutablecopy返回可变对象,copy返回不可变对象
6> 如何让自定义类可以使用copy修饰符
实现<NSCopying>协议,重写copyWithZone方法
3.全局&静态变量
1.全局变量和静态变量的区别
1> 修饰符
全局变量在声明源文件之外使用,需要extern引用一下;
静态变量使用static来修饰
2> 存储地址
两者都是存储在静态存储区,非堆栈上,它们与局部变量的存储分开
3> 生命周期
两者都是在程序编译或加载时由系统自动分配的,程序结束时消亡
4> 外部可访问性
全局变量在整个程序的任何地方均可访问,而静态变量相当于面向对象中的私有变量,他的可访问性只限定于声明它的那个源文件,即作用于仅局限于本文件中
五.runtime/消息转发机制
1> 什么是runtime
runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。
在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者,objc_msgSend
2> runtime干什么用,使用场景
runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)
在程序运行过程中, 动态创建一个类(比如KVO的底层实现) objc_allocateClassPair,class_addIvar,objc_registerClassPair
在程序运行过程中, 动态地为某个类添加属性方法, 修改属性值方法(修改封装的框架) objc_setAssociatedObject object_setIvar
遍历一个类的所有成员变量(属性)所有方法(字典转模型,归解档) class_copyIvarList class_copyPropertyList class_copyMethodList
2.消息机制
1> 消息转发的原理
当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。 通过这种方式,message与方法的真正实现在执行阶段才绑定。
为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。
缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点点点。
2> SEL isa super cmd 是什么
sel: 一种类型,表示方法名称,类似字符串(可互转)
isa:在方法底层对应的objc_msgSend调用时,会根据isa找到对象所在的类对象,类对象中包含了调度表(dispatch table),该表将类的sel和方法的实际内存地址关联起来
super_class:每一个类中还包含了一个super_class指针,用来指向父类对象
_cmd在Objective-C的方法中表示当前方法的selector,正如同self表示当前方法调用的对象实例
IMP定义为 id (*IMP) (id, SEL, …)。这样说来, IMP是一个指向函数的指针,这个被指向的函数包括id(“self”指针),调用的SEL(方法名),再加上一些其他参数.说白了IMP就是实现方法
3> 动态绑定
—在运行时确定要调用的方法
动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的 调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因 子负责确定消息的接收者和被调用的方法。运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者 的isa指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。
六.内存管理
1.内存区域
1>堆和栈的区别
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈是向低地址扩展的数据结构,是一块连续的内存的区域,栈顶的地址和栈的最大容量是系统预先规定好的,能从栈获得的空间较小。
堆是向高地址扩展的数据结构,是不连续的内存区域,因为系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存,因此堆获得的空间比较灵活,也比较大。
碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:
堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
2> iOS内存区域
2.1> 栈区
由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.
2.2> 堆区
一般由程序员分配释放,若程序员不释放,程序结束时由系统回收
2.3> 全局区(静态区)
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量相邻的另一块区域.
全局区分为未初始化全局区: .bss段 和初始化全局区: data段.
2.4> 常量区
常量字符串就是放在常量区
2.5> 代码区
存放函数体的二进制代码
2. 字符串的内存管理
创建字符串的内存空间 堆 常量区
3. 你是如何优化内存管理
1> 使用ARC
2> 延迟加载 懒加载
3> 重用 在正确的地方使用reuseIndentifier
4> 缓存 NSCache 保存计算数据
5> 处理内存警告 移除对缓存,图片 object 和其他一些可以重创建的 objects 的强引用
5.1> app delegate 中使用 `applicationDidReceiveMemoryWarning:` 的方法
5.2> 自定义 UIViewController 的子类 (subclass) 中覆盖 `didReceiveMemoryWarning`
5.3> 在自定义类中注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
6> 重用大开销对象 NSDateFormatter 和 NSCalendar 懒加载/单例 _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy”; 设置和创建速度一样慢
7> 自动释放池 手动添加自动释放池
8> 是否缓存图片 imageNamed imageWithContentOfFile
9> 混编
10> 循环引用 delegate block nstimer
11> 移除 kvo nsnotificationcenter 并未强引用,只记录内存地址,野指针报错 UIViewController自动移除 一般在dealloc中
13> performselector 延迟操作 [NSObject cancelPreviousPerformRequestsWithTarget:self]
4. 循环引用
delegate属性的内存策略
block循环引用 实际场景
5. autorelease的使用
1> 工厂方法为什么不释放对象
很多类方法为了在代码块结束时引用的对象不会因无强引用而被释放内存采用自动释放的方式,当其最近的自动释放池释放时该对象才会释放.
2> ARC下autorelease的使用场景
ARC中手动添加autoreleasepool可用于提前释放使用自动释放策略的对象,防止大量自动释放的对象堆积造成内存峰值过高.
3> 自动释放池如何工作
自动释放池时栈结构,每个线程的runloop运行时都会自动创建自动释放池,程序员可以代码手动创建自动释放池,自动释放的对象会被添加到最近的(栈顶)自动释放池中,系统自动创建的自动释放池在每个运行循环结束时销毁释放池并给池中所有对象发release消息,手动创建释放池在所在代码块结束时销毁释放池并发消息统一release
避免内存峰值
SDWebimage中加载gif图片 大循环
栈结构 栈顶
统一发release消息
5> ARC和MRC的混用
5.1> MRC>ARC
把MRC的代码转换成ARC的代码,删除内存管理操作(手动)
xcode提供了自动将MRC转换成ARC的功能,操作菜单栏edit -> Refacotor(重构) -> Convert to Objective-C ARC
5.2> ARC>MRC
在ARC项目中继续使用MRC编译的类,在编译选项中标识MRC文件即可"-fno-objc-arc"
在MRC项目中继续使用ARC编译的类在编译选项中标识MRC文件即可"-fobjc-arc”
6> NSTimer的内存管理
以下代码有什么问题?
@interface SvCheatYourself () { NSTimer *_timer; } @end @implementation SvCheatYourself - (id)init { self = [super init]; if (self) { _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testTimer:) userInfo:nil repeats:YES]; } return self; } - (void)dealloc { [_timer invalidate]; } - (void)testTimer:(NSTimer*)timer{ NSLog(@"haha!"); } @end
1)timer都会对它的target进行retain,对于重复性的timer,除非手动关闭,否则对象不会释放,场景:导航控制器关联的控制器无法销毁
2)NSTimer要加到异步线程中,防止线程繁忙导致定时器失准
3)timer必须加入到runloop中才会有效,主线程runloop默认开启,异步线程手动启动
4)注意runloop模式
7> ARC的实现原理
在程序预编译阶段,将 ARC 的代码转换为非 ARC 的 代码,自动加入 release、autorelease、retain
3.跨平台
3> OC和C框架对象引用
oc和c 桥接 三个桥接关键字都是干么的 __bridge 不更改归属权 __bridge_transfer 所有权给OC __bridge_retain 解除OC的所有权
ios5/6/7/8 内存方面的区别
ios5.自动引用计数 (ARC)
ios6.UICollectionView ( 内存重用机制,图片展示瀑布流实现 ) 在didReceiveMemoryWarning中处理内存(6之前在ViewDidUnload中) http://blog.csdn.net/likendsl/article/details/8199350
ios7.iOS7以后强制使用ARC
ios8
4.MRC
七.数据传递
1.block
1> block属性为什么用copy?
栈->堆
2> block使用注意什么?
循环引用
__block 修饰局部变量,这个变量在 block 内外属于同一个地址 上的变量,可以被 block 内部的代码修改
3> block的主要使用场景 ?
动画
数组字典排序遍历
回调状态
错误控制
多线程GCD
4>block原理
block属性是指向结构体的指针,
2.Delegate
4> 什么时候用delegate,什么时候用Notification
delegate针对one-to-one关系,并且reciever可以返回值给sender,notification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到 reciever的某个功能反馈值,notification用于通知多个object某个事件。
5> delegate和block
block使代码更紧凑,便于阅读,delegate可以设置必选和可选的方法实现,相比block
block可以访存局部变量. 不需要像以前的回调一样,把在操作后所有需要用到的数据封装成特定的数据结构, 你完全可以直接访问局部变量.
3.KVC和KVO
1> 如何调用私有变量 如何修改系统的只读属性 KVC的查找顺序
KVC在某种程度上提供了访问器的替代方案。不过访问器方法是一个很好的东西,以至于只要是有可能,KVC也尽量再访问器方法的帮助下工作。为了设置或者返回对象属性,KVC按顺序使用如下技术:
①检查是否存在-<key>、-is<key>(只针对布尔值有效)或者-get<key>的访问器方法,如果有可能,就是用这些方法返回值;
检查是否存在名为-set<key>:的方法,并使用它做设置值。对于 -get<key>和 -set<key>:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;
②如果上述方法不可用,则检查名为-_<key>、-_is<key>(只针对布尔值有效)、-_get<key>和-_set<key>:方法;
③如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:<key>或_<key>;
④如果仍为找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
2> 什么是键-值,键路径是什么
模型的性质是通过一个简单的键(通常是个字符串)来指定的。视图和控制器通过键来查找相应的属性值。在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。
键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是相对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性质。通过键路径,您可以指定对象图中的一
个任意深度的路径,使其指向相关对象的特定属性。
3> 什么是KVC
3> 什么是KVO
4> kvo的实现机制
当某个类的对象第一次被观察时,系统就会在运行时动态地创建该类的一个派生类,在这个派生类中重写原类中被观察属性的setter方法,派生类在被重写的setter方法实现真正的通知机制(Person->NSKVONotifying_Person).
派生类重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的isa指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对setter的调用就会调用重写的setter,从而激活键值通知机制。此外,派生类还重写了dealloc方法来释放资源。
5> KVO计算属性 设置依赖键
监听的某个属性可能会依赖于其它多个属性的变化(类似于swift,可以称之为计算属性),不管所依赖的哪个属性发生了变化,都会导致计算属性的变化,此时该属性如果不能通过set方法来监听(如get中进行计算
- (NSString *)accountForBank { return [NSString stringWithFormat:@“%@ for %@", self.accountName, self.bankCodeEn]; }
),则可以设置依赖键,两种方法:
1>
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; if ([key isEqualToString:@"accountForBank"]) { keyPaths = [keyPaths setByAddingObjectsFromArray:@[@"accountName", @"bankCodeEn"]]; } return keyPaths; }
2>
+ (NSSet *)keyPathsForValuesAffectingAccountForBank { return [NSSet setWithObjects:@"accountBalance", @"bankCodeEn", nil]; }
6> KVO集合属性
可对可变集合的元素改变进行监听(如添加、删除和替换元素),使用集合监听对象
使用KVO
重写class
5> kvo使用场景
①实现上下拉刷新控件 contentoffset
②webview混合排版 contentsize
③监听模型属性实时更新UI
4.栈结构&队列
栈结构先进后出,iOS中有navigationcontroller的子控制器,autoreleasepool
队列先进先出,
八.其他
1.int和NSInteger的区别
NSInteger表示当前cpu下整型所占最大字节,不同CPU的long型所占字节不同,32位int4 long4,64位int4,long8
2.import和include
import可以避免重复包含
3.@class
避免循环引用头文件
UI
一.控件
1.属性
1> frame和bounds的区别
frame:可表示尺寸和位置,与父视图坐标系的关系,位置以自己的左上角为原点,可用于形变和位移
bounds:可表示尺寸和位置,与自身视图坐标系的关系,大多数情况(滚动视图的子视图等除外)以自己的中心点为原点,可用于形变
center:只表示位置,表示自己中心的坐标,可用于位移
2> trasform
修改位移形变旋转,transform不同于boardcenterframe,前者中记录的是形变的数据,不发生形变其值是空的,所以我们需要新建结构体,用CGAffineTransform(仿射变换)函数给对象结构体属性赋值,而后者是控件的固有属性,内存数据是始终存在的,当我们用他们做移动等操作时,是改变其值,所以是结构体赋值三步曲,不用CG的函数
使用情景区别: transform一般用于有来有回的变化,而frame是有去无回
2.UIScrollView
1> contentsize、contentoffset、contentinset的区别
内容视图的尺寸
内容视图当前位置相对滚动视图frame的偏移量
内容视图相对滚动视图frame的展示原点
3.UITableview
1> 自定义高度
1.1>新建一个继承自UITableViewCell的类
1.2>重写initWithStyle:reuseIdentifier:方法
1.3>添加所有需要显示的子控件(不需要设置子控件的数据和frame, 子控件要添加到contentView中)
1.4>进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体固定的图片)
1.5>提供2个模型
数据模型: 存放文字数据图片数据
frame模型: 存放数据模型所有子控件的framecell的高度
1.6>cell拥有一个frame模型(不要直接拥有数据模型)
1.7>重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame
2> 自定义高度原理
A 手动计算
1> 由于heightForRow比cellForRow方法先调用,创建frame模型包含微博模型,重写微博模型赋值set方法,提前计算cell子控件的frame并保存,heightForRow方法中取出frame模型中保存的高度,实现自定义高度cell
2> 设置最大尺寸、文本属性,根据文本内容计算正文内容展示尺寸
3> cellForRow中创建自定义cell包含frame属性,重写frame属性set方法创建cell子控件并赋值frame模型保存的子控件尺寸
B. 自动计算
1> 首先设置行高使用autolayout自动计算并预估高度
2> 在stroboard中对cell内容进行自动布局,注意设置图片距离底部约束,cellForRow中创建storyboard中对应标记的自定义cell
3> 由于正文内容的不确定性,设置label多行,拖线图片高度约束,根据图片有无,设置代码设置高度约束
4.UICollectionView
1> 如何实现瀑布流,流水布局
1.1> 使用UICollectionView
1.2> 使用自定义的FlowLayout
1.3> 需要在layoutAttributesForElementsInRect中设置自定义的布局(item的frame)
1.4> 在 prepareLayout中计算布局
1.5> 遍历数据内容,根据索引取出对应的attributes(使用layoutAttributesForCellWithIndexPath),根据九宫格算法设置布局
1.6> 细节1: 实时布局,重写shouldInvalidateLayoutForBoundsChange(bounds改变重新布局,scrollview的contentoffset>bounds)
1.7> 细节2: 计算设置itemsize(保证内容显示完整,uicollectionview的content size是根据itemize计算的),根据列最大高度/对应列数量求出,最大高度累加得到
1.8> 细节3: 追加item到最短列,避免底部参差不齐.
2> 和UITableView的使用区别
1)必须使用下面的方法进行Cell类的注册:
- (void)registerClass:forCellWithReuseIdentifier: - (void)registerClass:forSupplementaryViewOfKind:withReuseIdentifier: - (void)registerNib:forCellWithReuseIdentifier:
2)collectionView与tableView最大的不同点,collectionView必须要使用自己的layout(UICollectionViewLayout)
如:
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; flowLayout.itemSize = CGSizeMake(52, 52); // cell大小 flowLayout.minimumInteritemSpacing = 1; // cell间距 flowLayout.minimumLineSpacing = 1; // cell行距 flowLayout.sectionInset = (UIEdgeInsets){81,1,1,1}; // cell边距
创建collectionView需要带Layout的初始化方法:
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
5.UIImage
1> 有哪几种加载方式
1.1> 二进制 imageWithData
1.2> Bundle imageWithName
1.3> 本地路径 imageWithContentOfFile
1.4>
6.webview
1>解决webview的内存占用和泄露
7.描述九宫格算法
1> 1> 根据格子宽appW高appH和每行格数totalCol计算格子间隙marginX
CGFloat marginX = (self.view.frame.size.width - totalCol * appW)/(totalCol + 1);
2> 2> 根据序号i和每行格数totalCol计算行号列号
int row = i / totalCol; int col = i % totalCol;
3> 3> 根据格子间隙、格子宽高和行号列号计算x,y
CGFloat appX = marginX + col * (appW + marginX);
CGFloat appY = row * (appH + marginY);
8. 实现图片轮播图
1> 1> UIScrollView设置contentSize,添加图片并设置frame,设置分页
2> 2> 添加分页控制器,在UIScrollView滚动代理方法中根据contentOffset计算当前页数并设置
3> 设置定时器,主动改变contentOffset,设置定时器的模式进行并发操作(终极方案定时器放在异步线程)
二.生命周期
1> 应用的生命周期
/// 各个程序运行状态时代理的回调: - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions //告诉代理进程启动但还没进入状态保存 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions //告诉代理启动基本完成程序准备开始运行 - (void)applicationWillResignActive:(UIApplication *)application //当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了 - (void)applicationDidBecomeActive:(UIApplication *)application //当应用程序入活动状态执行,这个刚好跟上面那个方法相反 - (void)applicationDidEnterBackground:(UIApplication *)application // 当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可 - (void)applicationWillEnterForeground:(UIApplication *)application //当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反。 - (void)applicationWillTerminate:(UIApplication *)application //当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。
2> 视图的生命周期
loadView // - 默认调用super方法,根据控制器创建方式加载视图,重写后将根据重写方法创建视图 viewDidLoad // -视图加载完成 viewWillAppear-UIViewController // -对象的视图即将加入窗口时调用; viewDidApper-UIViewController // -对象的视图已经加入到窗口时调用; viewWillDisappear-UIViewController // -对象的视图即将消失、被覆盖或是隐藏时调用; viewDidDisappear-UIViewController // - 对象的视图已经消失、被覆盖或是隐藏时调用; viewVillUnload //-当内存过低时,需要释放一些不需要使用的视图时,即将释放时调用; viewDidUnload //-当内存过低,释放一些不需要的视图时调用。
3> load initialize方法的区别
+(void)load | +(void)initialize | |
执行时机 | 在程序运行后立即执行 | 在类的方法第一次被调时执行 |
若自身未定义,是否沿用父类的方法? | 否 | 是 |
类别中的定义 | 全都执行,但后于类中的方法 | 覆盖类中的方法,只执行一个 |
4> 创建控制器、视图的方式
4.1> 创建控制器的方式
1)通过代码的方式加载viewController
UIViewController *controller = [[UIViewController alloc] init];
2)通过stroyboard来加载viewController
2.1) 加载storyboard中箭头指向的viewController
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; //加载箭头指向的viewController CZViewController *controller = [storyboard instantiateInitialViewController];
2.2) 加载storyboard中特定标示的viewController(storyboard可以有多个controller)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; CZViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"two"];
3)通过xib加载viewController
3.1) 传统方法
3.1.1)创建Xib,并指定xib的files owner为自定义控制器类(为了能连线关联管理IB的内容)
3.1.2)xib中要有内容,且xib中描述的控制器类的view属性要与xib的view控件完成关联(关联方法两种,一种是control+files owner拖线到xib中搭建的指定view控件,另一种是指定xib中的view拖线到@interface)
3.1.3)从xib加载viewController
CZViewController *controller = [[CZViewController alloc] initWithNibName:@"CZOneView" bundle:nil];
3.2)bundle中取出xib内容
CZViewController *vc = [[NSBundle mainBundle] loadNibNamed:@"Two" owner:nil options:nil].lastObject;
4.2> 创建视图的方式
1.用系统的loadView方法创建控制器的视图
2.如果指定加载某个storyboard文件做控制器的视图,就会加载storyboard里面的描述去创建view
3.如果指定读取某个xib文件做控制器的视图,就根据指定的xib文件去加载创建
4.如果有xib文件名和控制器的类名前缀(也就是去掉controller)的名字一样的 xib文件 就会用这个xib文件来创建控件器的视图 例:控件器的名为 MJViewController xib文件名为 MJView.xib 如果xib文件名后有一个字不一样就不会去根据它去创建如:MJView8.xib
5.找和控制器同名的xib文件去创建
6.如果以上都没有就创建一个空的控制器的视图;
5> UIWindow
是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手 动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
1、作为容器,包含app所要显示的所有视图
2、传递触摸消息到程序中view和其他对象
3、与UIViewController协同工作,方便完成设备方向旋转的支持
三.多控制器管理
1.
四.核心绘图
6> View和layer的区别
图层不会直接渲染到屏幕上,UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由CoreAnimation来实现的。它真正的绘图部分,是由一个CALayer类来管理。UIView本身更像是一个CALayer的管理器。一个UIView上可以有n个CALayer,每个layer显示一种东西,增强UIView的展现能力。
6.1>都可以显示屏幕效果
6.2> 如果需要用户交互就要用UIVIew,其可接收触摸事件(继承UIResponder),而CALayer不能接收触摸事件
6.3> 如果没有用户交互可选用CALayer,因为其所在库较小,占用的资源较少
7> new和alloc init的区别
采用new的方式只能采用默认的init方法完成初始化,采用alloc的方式可以用其他定制的初始化方法。
五.动画
1> ios界面切换
2> iOS中各种动画的类型&特点&使用场景
CAPropertyAnimation
是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation
属性解析:
keyPath:通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@”position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果
CABasicAnimation
CAPropertyAnimation的子类
属性解析:
fromValue:keyPath相应属性的初始值
toValue:keyPath相应属性的结束值
随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue
如果fillMode=kCAFillModeForwards和removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。比如,CALayer的position初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为(100,100),虽然动画执行完毕后图层保持在(100,100)这个位置,实质上图层的position还是为(0,0)
CAKeyframeAnimation
CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
属性解析:
values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path:可以设置一个CGPathRefCGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略
keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的
CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation
CAAnimationGroup
CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行
属性解析:
animations:用来保存一组动画对象的NSArray
默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间
CATransition
CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点
UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果
属性解析:
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点(在整体动画的百分比)
endProgress:动画终点(在整体动画的百分比)
UIView动画
UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持
执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码放在[UIView beginAnimations:nil context:nil]和[UIView commitAnimations]之间
Block动画
帧动画
六.事件处理
1> 描述响应者链条
当触摸事件发生时,压力转为电信号,iOS系统将产生UIEvent对象,记录事件产生的时间和类型,然后系统将事件加入到一个由UIApplication管理的事件队列中。
UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)
主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件(从父到子,从后到前),这也是整个事件处理过程的第一步
找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理
4.Runloop
1> 每个线程上都有一个runloop,主线程默认开启,辅助线程需要手动开启,主要用于
- 使用端口或自定义输入源来和其他线程通信
- 使用线程的定时器
- Cocoa中使用任何performSelector…的方法
- 使线程周期性工作
2> runloop的工作流程
七.屏幕适配
多线程
一.资源抢夺
2> 资源抢夺解决方案
@sychronized{ }
dispatch_barrier_async
NSLock NSCondition
dispatch_semaphore_wait
二.iOS多线程技术
3> 对比iOS中的多线程技术
3.1> pthread
pthread跨平台,使用难度大,需要手动管理线程生命周期
pthread_create创建线程,传参线程标记,线程属性,初始函数,函数参数
3.2> NSThread
NSThread需要手动管理线程生命周期和
3.3> GCD
3.4> NSOperation
GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
3.2> GCD仅仅支持FIFO队列,只可以设置队列的优先级,而NSOperationQueue中的每一个任务都可以被重新设置优先级(setQueuePriority:),从而实现不同操作的执行顺序调整
3.3> GCD不支持异步操作之间的依赖关系设置。如果某个操作的依赖另一个操作的数据,使用NSOperationQueue能够设置依赖按照正确的顺序执行操作(addDependency:)。GCD则没有内建的依赖关系支持(只能通过Barrior和同步任务手动实现)。
3.4> NSOperationQueue方便停止队列中的任务(cancelAllOperations, suspended),GCD不方便停止队列中的任务.
3.5> NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
3.6> GCD的执行速度比NSOperationQueue快
3.7> NSOperationQueue可设置最大并发数量(节电),GCD具有dispatch_once(只执行一次,单例)和dispatch_after(延迟执行)功能
3.8> NSObject分类(perform)和NSThread遇到对象分配需要手动内存管理,手动管理线程生命周期
3.9> NSThread查看线程
3.10> NSObject分类线程通信
4> 原子属性
原子属性采用的是"多读单写"机制的多线程策略
"多读单写"缩小了锁范围,比互斥锁的性能好
规定只在主线程更新UI,就是因为如果在多线程中更新,就需要给UI对象加锁,防止资源抢占写入错误,但是这样会降低UI交互的性能,所以ios设计让所有UI对象都是非线程安全的(不加锁),并规定只在主线程中更新UI,规避多线程抢占资源问题
三.其他
1> 多线程优缺点
优点:
使应用程序的响应速度更快,用户界面在进行其他工作的同时仍始终保持活动状态;
优化任务执行,适当提高资源利用率(cpu, 内存);
缺点:
线程占用内存空间,管理线程需要额外的CPU开销,开启大量线程,降低程序性能;
增加程序复杂度,如线程间通信,多线程的资源共享等;
2> 在多线程中使用通知需要注意什么问题?
3> iOS中的延迟操作
1>
[self performSelector:@selector(clearCache) withObject:nil afterDelay:duration];
2>
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{..});
网络
一.网络基础
1.数据解析
1> XML解析方式
SAX 方式解析
-只读
-速度快
-从上向下
-通过5个代理方法解析,每个代理方中都需要写一些代码!
-如果要实现SAX解析,思路最重要!
-适合比价大的XML的解析
DOM解析的特点
-一次性将XML全部加载到内存,以树形结构
-好处,可以动态的修改,添加,删除节点
-内存消耗非常大!尤其横向节点越深!
-iOS默认不支持 DOM 解析!
-在 MAC 端,或者服务器端开发,都基本上使用 DOM 解析
-在 iOS 端如果需要使用 DOM 方式解析,可以使用第三方框GData/KissXML(XMPP)
-适合比较小的 XML 文件
-在 MAC 中,苹果提供了一个 NSXML 的类,能够做 DOM 解析,在 iOS 不能使用!
2> json&xml的区别
1)解码难度: json的解码难度基本为零,xml需要考虑子节点和父节点
2)数据体积&传输速度: json相对于xml来讲,数据体积小,json的速度远远快于xml
3)数据交互: json与JavaScript的交互更加方面,更容易解析处理,更好的数据交互
4)数据描述: xml对数据描述性比较好
2.网络协议
1>TCP如何防止乱序和丢包
TCP数据包的头格式中有两个概念,Sequence Number是数据包的序号,用来解决网络包乱序(reordering)问题。Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。
位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)Sequence number(顺序号码) Acknowledge number(确认号码).
SeqNum的增加是和传输的字节数相关的,TCP传输数据时,A主机第一次传输1440个字节,seq=1,那么第二次时seq = 1441,B拼接数据就是根据seq进行拼接的,seq数字不断累加避免了乱序.B主机收到第一次数据包以后会返回ack = 1441.
A主机收到B的ack = 1441时,就知道第一个数据包B已收到. 如果B没有收到第一次的数据包,那么B再收到A的数据包时,他就会发ack = 1回去,A收到B的回复,发现B没有收到第一次数据包,就会重发第一次数据包,这样就可以防止丢包.
2>描述一下三次握手
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据.
3> TCP与UDP的区别:
3.1>基于连接与无连接;
3.2>对系统资源的要求(TCP较多,UDP少);
3.3>UDP程序结构较简单;
3.4>流模式与数据报模式 ;
3.5>TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
4>http和scoket通信的区别
http是客户端用http协议进行请求,发送请求时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配合(当然也非绝对)。 http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取http长连接技术)。iPhone主要使用类是NSUrlConnection。
scoket是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是CFSocketRef。
5> HTTP请求常用的几种方式
GET :获取指定资源
POST :2M 向指定资源提交数据进行处理请求,在RESTful 风格用于新增资源
HEAD :获取指定资源头部信息
PUT :替换指定资源(不支持浏览器操作)
DELETE: 删除指定资源
3.网络传输
1>DNS是如何工作的
DNS是domain name server的简称,每个网络的计算机都有ip,但是不好记,所以用域名替代(如www.baidu.com),在 Internet 上真实在辨识机器的还是 IP,所以当使用者输入Domain Name 后,浏览器必须要先去一台有 Domain Name 和IP 对应资料的主机去查询这台电脑的 IP,而这台被查询的主机,我们称它为 Domain Name Server,简称DNS,例如:当你输入www.pchome.com.tw时,浏览器会将www.pchome.com.tw这个名字传送到离他最近的 DNS Server 去做辨识,如果查到,则会传回这台主机的 IP,进而跟它索取资料,但如果没查到,就会发生类似 DNS NOT FOUND 的情形,所以一旦DNS Server当机,就像是路标完全被毁坏,没有人知道该把资料送到那里
2> POST请求常见的数据格式
二.网络安全/加密
三.数据存储
1.数据存储技术
1> 数据存储的几种方式
XML属性列表(plist)归档
Preference(偏好设置)
NSKeyedArchiver归档(NSCoding)
SQLite3
Core Data
2> 各自特点
Plist:
属性列表是一种XML格式的文件,拓展名为plist
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中
将一个NSDictionary对象归档到一个plist属性列表中
// 将数据封装成字典 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"母鸡" forKey:@"name"]; // 将字典持久化到Documents/stu.plist文件中 [dict writeToFile:path atomically:YES];
面试考点:
1. plist的根节点 只能是NSDictionary和NSArray,所以存储内容必须转为对象类型
2. 使用场景 功能动态更新 应用级别数据更新 XML的替代品
偏好设置:
每个应用都有个NSUserDefaults实例,通过它来存取偏好设置
比如,保存用户名、字体大小、是否自动登录
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:@"itcast" forKey:@"username"]; [defaults setFloat:18.0f forKey:@"text_size"]; [defaults setBool:YES forKey:@"auto_login"];
面试考点:
1. 使用场景 保存应用信息
2. 特点 不会自动删除,itune同步,不适合存大数据
3. 使用单例模式、
4. 直接存取结构体,基本数据类型,无需转换
5. 即时操作注意同步
归档:
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复
不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以
NSCoding协议有2个方法:
encodeWithCoder:
每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
initWithCoder:
每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量
// 归档一个NSArray对象到Documents/array.archive NSArray *array = [NSArray arrayWithObjects:@”a”,@”b”,nil]; [NSKeyedArchiver archiveRootObject:array toFile:path];
使用archiveRootObject:toFile:方法可以将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象
归档(编码)
// 新建一块可变数据区 NSMutableData *data = [NSMutableData data]; // 将数据区连接到一个NSKeyedArchiver对象 NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; // 开始存档对象,存档的数据都会存储到NSMutableData中 [archiver encodeObject:person1 forKey:@"person1"]; [archiver encodeObject:person2 forKey:@"person2"]; // 存档完毕(一定要调用这个方法) [archiver finishEncoding]; // 将存档的数据写入文件 [data writeToFile:path atomically:YES];
l 恢复(解码)
// 从文件中读取数据 NSData *data = [NSData dataWithContentsOfFile:path]; // 根据数据,解析成一个NSKeyedUnarchiver对象 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; Person *person1 = [unarchiver decodeObjectForKey:@"person1"]; Person *person2 = [unarchiver decodeObjectForKey:@"person2"]; // 恢复完毕 [unarchiver finishDecoding];
利用归档实现深复制
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1]; // 解析data,生成一个新的Person对象 Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
面试考点:
1. 特点: 存入Document,itune同步,不会自动删除,可存放大型用户数据
2. 使用场景: 用户产生的数据,如游戏,操作记录等等
3. 可保存自定义对象,需要遵守NSCoding协议,实现对应的encodeWithCoder initWithCoder 方法
4. 和NSData的配合
4.1> 多对象单目录存储
4.2> 字典/数组内容的深拷贝
5. 不能直接存基本类型和结构体,需要转成对象 NSValue NSNumber
2> 沙盒目录结构
2.1> Library Caches Preferences
2.2> Documents
2.3> tmp
3> 如何读取沙盒中plist的内容
1> 3.1> 读取沙盒并拼接plist的文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
3.2> 根据plist根节点类型读取plist文件
NSArray *apps = [NSArray arrayWithContentsOfFile:path];
2.数据库技术(SQLite&CoreData)
1> SQLite和CoreData的区别
1.1> CoreData可以在一个对象更新时,其关联的对象也会随着更新,相当于你更新一张表时,其关联的其他表的也回随着更新
1.2> CoreData提供更简单的性能管理机制,可以限制查询记录的总数,这个类会自动更新其缓存
1.3> 多表查询方面,CoreData没有SQL直观,没有类似外连接,左连接等操作.
四.Html5/oc&js互调
// oc>js: [self.webView stringByEvaluatingJavaScriptFromString:“window.location.href = xxx”]; // js>oc: 利用hmtl中js的重定向技术 <Script> window.location.href = www.baidu.com//method:dosomething </Script> // 使用方法截取重定向 (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
五.iOS网络框架
1> NSURLConnection和NSURLSession的区别
1.1> 异步请求不需要NSOperation包装
1.2> 支持后台运行的网络任务(后台上传下载)
1.3> 根据每个Session做配置(http header,Cache,Cookie,protocal,Credential),不再在整个App层面共享配置
1.4> 支持网络操作的取消和断点续传(继承系统类,重新main方法)
1.5> 改进了授权机制的处理
项目
1.实用技术
2.知名第三方框架
3.开发技巧
1> description方法
Swift
1> Swift和OC的区别
1.1> Swift没有地址/指针的概念
1.2> 泛型
1.3> 类型严谨 对比oc的动态绑定
6.设计模式
1> 常用的设计模式
代理 观察者 工厂 单例 策略
2> 代理属性的内存策略是什么,为什么?
3> 观察者模式的使用场景
4> 工厂模式(类方法)为什么没有释放对象? autorelease工作原理? arc下还需要手动使用autorelease吗?为什么?什么场景?
5> 手写单例
6> 策略 cell多种响应效果 代理方法
(一)代理模式
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
列表row个数delegate
自定义的delegate
(二)观察者模式
应用场景:一般为model层对controller和view进行的通知方式,不关心谁去接收,只负责发布信息。
优势:解耦合
敏捷原则:接口隔离原则,开放-封闭原则
实例:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
kvo,键值对改变通知的观察者,平时基本没用过。
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例: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的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。
(六)工厂模式
应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
应用场景:是一中非常古老的设计模式,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优势:使系统,层次清晰,职责分明,易于维护
敏捷原则:对扩展开放-对修改封闭
实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。
(四)单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:使用简单,延时求值,易于跨模块
敏捷原则:单一职责原则
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,返回的也只是此单例类的唯一静态变量。
(五)策略模式
应用场景:定义算法族,封装起来,使他们之间可以相互替换。
优势:使算法的变化独立于使用算法的用户
敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。
实例:排序算法,NSArray的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。
(六)工厂模式
应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
增 加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。
2> MVVM和rectivecocoa
六.框架
1.SDWebimage
1> SDWebimage的缓存机制
- UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显示 placeholderImage ,同时由SDWebImageManager 根据 URL 来在本地查找图片。
- SDWebImageManager: downloadWithURL:delegate:options:userInfo: SDWebImageManager是将UIImageView+WebCache同SDImageCache链接起来的类, SDImageCache: queryDiskCacheForKey:delegate:userInfo:用来从缓存根据CacheKey查找图片是否已经在缓存中
- 如果内存中已经有图片缓存, SDWebImageManager会回调SDImageCacheDelegate : imageCache:didFindImage:forKey:userInfo:
- 而 UIImageView+WebCache 则回调SDWebImageManagerDelegate: webImageManager:didFinishWithImage:来显示图片。
- 如果内存中没有图片缓存,那么生成 NSInvocationOperation 添加到队列,从硬盘查找图片是否已被下载缓存。
- 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调
notifyDelegate: - 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:进而回调展示图片。
- 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调
imageCache:didNotFindImageForKey:userInfo: - 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
- 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
- connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
- connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
- 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
- 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
- imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
- 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
- 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。
- 写文件到硬盘在单独 NSInvocationOperation 中完成,避免拖慢主线程。
- 如果是在iOS上运行,SDImageCache 在初始化的时候会注册notification 到 UIApplicationDidReceiveMemoryWarningNotification 以及 UIApplicationWillTerminateNotification,在内存警告的时候清理内存图片缓存,应用结束的时候清理过期图片。
- SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
位运算
NSCache
特点: a> 线程安全的 b> 当内存不足的时候,自动释放 c> 缓存数量和缓存成本
区别NSMutableDictionary
1> 不能也不应该遍历 2> NSCache对key强引用,NSMutableDictionary对key进行copy
2.AFN
1>实现原理
AFN的直接操作对象AFHTTPClient不同于ASI,是一个实现了NSCoding和NSCopying协议的NSObject子类。 AFHTTPClient是一个封装了一系列操作方法的“工具类”,处理请求的操作类是一系列单独的,基于NSOperation封装 的,AFURLConnectionOperation的子类。AFN的示例代码中通过一个静态方法,使用dispatch_once()的方式创建 AFHTTPClient的共享实例,这也是官方建议的使用方法。在创建AFHTTPClient的初始化方法中,创建了OperationQueue并 设置一系列参数默认值。在getPath:parameters:success:failure方法中创建NSURLRequest,以 NSURLRequest对象实例作为参数,创建一个NSOperation,并加入在初始化发方中创建的NSOperationQueue。以上操作都 是在主线程中完成的。在NSOperation的start方法中,以此前创建的NSURLRequest对象为参数创建NSURLConnection 并开启连结。
2> 传递指针 如何使一个方法返回多个返回值
传参指针变量的地址,方法内部通过*运算符使用该地址可以修改该地址保存的内容(引用对象的地址),当外部再次使用该指针变量取出引用对象时,引用对象已经在方法内部发生了改变,指针变量指向其他数据,相当于方法的返回值(经方法处理后生成的外部可使用的结果数据).
七.项目
1.编译链接
1> id和instancetype的区别
instancetype只能做返回值,编译时判断真实类型,不符合发警告
特殊情况: 关联类型返回方法,如类方法alloc或new开头,实例方法中,以autorelease,init,retain,或self开头
2.静态库
如何给静态库添加属性 分类+runtime
如何调用私有方法 performselector category(前向引用)
3.混编
arc mrc混编
c c++混编
4.加密
5.iOS更新
ios6 7 8的区别
5.日期处理
字符串操作 append format substring rangeofstring nsrange http://www.cnblogs.com/neworiginou/archive/2012/11/14/2770038.html
6.性能优化
1> 如何进行性能优化
1.1> 内存优化的点 重用 懒加载
1.2> 渲染优化 尽量使用不透明的图 把 views 设置为透明
1.3> 在ImageView设置前,尽量先调整好图片大小 尤其放在uiscrolliview中自动缩放耗能
1.4> 避免使用过大的xib 和分镜的区别 一次性加载
1.5> 不要阻塞主线程 除渲染,触摸响应等 尽量异步处理 如存储,网络 异步线程通知
1.6> 缓存 网络响应,图片,计算结果(行高) 网络响应NSUrlconnection默认缓存request,设置策略 非网络请求 使用nscache nsdictionary
1.7> 避免反复处理数据 在服务器端和客户端使用相同的数据结构
1.8> 选择正确的数据格式 json 速度快 解析方便 xml sax方式逐行解析 解析大文件不占用内存和损失性能
1.9> 优化tableview 重用cell 缓存行高 cell子视图尽量少且不透明
1.10> 选择正确的数据存储选项 plist nscoding NSUserDefaults sqlite coredata
八.算法
1.交换数值的几种方法 中间变量 加减法 异或
2.oc/c实现常用排序
3.
二叉树
链表
写一个单链表,要求可以插入数据和删除单个数据
递归