一.三大特性
1.封装_点语法
1> 本质//以下代码有什么问题- (void)setName:(NSString *)name { self.name = name; }- (NSString *)name { return self.name;}点语法的本质是调用类的getter方法和setter方法,如果类中没有getter方法和setter方法就不能使用点语法。
2.继承
1> 如何实现多重继承消息转发 forwardingTargetForSelector methodSignatureForSelector forwardInvocationdelegate和protocol 类别 http://www.cocoachina.com/ios/20130528/6295.html
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”]; //显示 abNSString *str3 = [NSString stringWithFormat:@“a\"b”]; //显示 a”b 反斜杠转义
2.NSArray和NSDictionary1> 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 settergetter=getterName,setter=setterName,设置setter与getter的方法名1.2> weak assign strong copyassign 用于非指针变量。用于基础数据类型 (例如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修饰符实现协议,重写copyWithZone方法
3.全局&静态变量
1.全局变量和静态变量的区别1> 修饰符 全局变量在声明源文件之外使用,需要extern引用一下; 静态变量使用static来修饰2> 存储地址 两者都是存储在静态存储区,非堆栈上,它们与局部变量的存储分开3> 生命周期 两者都是在程序编译或加载时由系统自动分配的,程序结束时消亡4> 外部可访问性 全局变量在整个程序的任何地方均可访问,而静态变量相当于面向对象中的私有变量,他的可访问性只限定于声明它的那个源文件,即作用于仅局限于本文件中
五.runtime/消息转发机制
1.runtime http://www.cocoachina.com/ios/20150715/12540.html
1> 什么是runtimeruntime是一套比较底层的纯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 nastier
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
ios84.MRC
七.数据传递
1.block
1> block属性为什么用copy?栈->堆
2> block使用注意什么?循环引用 __block 修饰局部变量,这个变量在 block 内外属于同一个地址 上的变量,可以被 block 内部的代码修改
3> block的主要使用场景 ?动画数组字典排序遍历回调状态错误控制多线程GCD4>block原理block属性是指向结构体的指针,
2.Delegate
4> 什么时候用delegate,什么时候用Notificationdelegate
针对one-to-one关系,并且reciever可以返回值给sender,notification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到 reciever的某个功能反馈值,notification用于通知多个object某个事件。
5> delegate和blockblock使代码更紧凑,便于阅读,delegate可以设置必选和可选的方法实现,相比blockblock可以访存局部变量. 不需要像以前的回调一样,把在操作后所有需要用到的数据封装成特定的数据结构, 你完全可以直接访问局部变量.
3.KVC和KVO
1> 如何调用私有变量 如何修改系统的只读属性 KVC的查找顺序KVC在某种程度上提供了访问器的替代方案。不过访问器方法是一个很好的东西,以至于只要是有可能,KVC也尽量再访问器方法的帮助下工作。为了设置或者返回对象属性,KVC按顺序使用如下技术:①检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值;检查是否存在名为-set:的方法,并使用它做设置值。对于 -get和 -set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;②如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_get和-_set:方法;③如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:或_;
④如果仍为找到,则调用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
避免循环引用头文件