美文网首页
OC的简单总结

OC的简单总结

作者: 蓝心儿的蓝色之旅 | 来源:发表于2015-12-24 00:03 被阅读635次

    一.OC的三大特性

    封装、继承、多态

    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> 分类的作用

    将类的实现分散到多个不同文件或多个不同框架中。

    创建对私有方法的前向引用。

    向对象添加非正式协议。

    4> 分类的局限性

    无法向类中添加新的实例变量,类别没有位置容纳实例变量。

    名称冲突,即当类别中的方法与原始类方法名称冲突时,类别具有更高的优先级。类别方法将完全取代初始方法从而无法再使用初始方法。

    无法添加实例变量的局限可以使用字典对象解决.

    三.Foundation

    1.字符串

    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

    一个区分度很大的面试题

    考察一个面试者基础咋样,基本上问一个 @property 就够了:

    @property 后面可以有哪些修饰符?

    线程安全的:

    atomic,nonatomic

    访问权限的

    readonly,readwrite

    内存管理(ARC)

    assign,strong,weak,copy

    内存管理(MRC)

    assign,retain,copy

    指定方法名称

    setter=

    getter=

    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、什么情况使用 weak 关键字,相比 assign 有什么不同?比如:

    在ARC中,出现循环引用的时候,必须要有一端使用weak,比如:自定义View的代理属性

    已经自身已经对它进行一次强应用,没有必要在强引用一次,此时也会使用weak,自定义View的子控件属性一般也使用weak;但b是也可以使用strong

    weak当对象销毁的时候,指针会被自动设置为nil,而assign不会* assigin 可以用非OC对象,而weak必须用于OC对象

    2、怎么用 copy 关键字?

    对于字符串和block的属性一般使用copy

    字符串使用copy是为了外部把字符串内容改了,不影响该属性

    block使用copy是在MRC遗留下来的,在MRC中,方法内部的block是在在栈区的,使用copy可以把它放到堆区.在ACR中对于block使用copy还是strong效果是一样的

    3、这个写法会出什么问题: @property (copy) NSMutableArray *array;

    添加,删除,修改数组内的元素的时候,程序会因为找不到对于的方法而崩溃.因为copy就是复制一个不可变NSArray的对象

    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方法

    五.runtime/消息转发机制

    1.runtimehttp://www.cocoachina.com/ios/20150715/12540.html

    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 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。

    5> 通知的内存管理 线程问题

    六.数据传递

    1.block

    1> block属性为什么用copy?

    栈->堆

    2> block使用注意什么?

    循环引用  修改外部变量

    3> block的主要使用场景 ?

    动画

    数组字典排序遍历

    回调状态

    错误控制

    多线程GCD

    4>block原理

    block属性是指向结构体的指针,

    2.Delegate

    1> 什么时候用delegate,什么时候用Notification

    delegate针对one-to-one关系,并且reciever可以返回值给sender,notification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到 reciever的某个功能反馈值,notification用于通知多个object某个事件。

    2> delegate和block

    block使代码更紧凑,便于阅读,delegate可以设置必选和可选的方法实现,相比block

    block可以访存局部变量. 不需要像以前的回调一样,把在操作后所有需要用到的数据封装成特定的数据结构, 你完全可以直接访问局部变量.

    3.KVC和KVO

    1> 如何调用私有变量    如何修改系统的只读属性    KVC的查找顺序

    KVC在某种程度上提供了访问器的替代方案。不过访问器方法是一个很好的东西,以至于只要是有可能,KVC也尽量再访问器方法的帮助下工作。为了设置或者返回对象属性,KVC按顺序使用如下技术:

    ①检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值;

    检查是否存在名为-set:的方法,并使用它做设置值。对于 -get和 -set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;

    ②如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_get和-_set:方法;

    ③如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:或_;

    ④如果仍为找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

    2> 什么是键-值,键路径是什么

    模型的性质是通过一个简单的键(通常是个字符串)来指定的。视图和控制器通过键来查找相应的属性值。在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。

    键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是相对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对象的特定属性。

    3> 什么是KVC和KVO

    KVC(Key-Value-Coding)内部的实现:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。KVO(Key-Value- Observing):当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以 isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名

    4> kvo的实现机制

    当某个类的对象第一次被观察时,系统就会在运行时动态地创建该类的一个派生类,在这个派生类中重写原类中被观察属性的setter方法,派生类在被重写的setter方法实现真正的通知机制(Person->NSKVONotifying_Person).

    派生类重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的isa指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对setter的调用就会调用重写的setter,从而激活键值通知机制。此外,派生类还重写了dealloc方法来释放资源。

    5> kvo使用场景

    ①实现上下拉刷新控件 contentoffset

    ②webview混合排版 contentsize

    ③监听模型属性实时更新UI

    六.设计模式

    1> 常用的设计模式

    代理  观察者  工厂  单例   策略

    2> 代理属性的内存策略是什么,为什么?

    3> 观察者模式的使用场景

    4> 工厂模式(类方法)为什么没有释放对象? autorelease工作原理? arc下还需要手动使用autorelease吗?为什么?什么场景?

    5> 手写单例

    6> 策略  cell多种响应效果   代理方法

    (一)代理模式

    应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。

    优势:解耦合

    敏捷原则:开放-封闭原则

    实例: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完成易用性替换

    注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,

    增加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。

    相关文章

      网友评论

          本文标题:OC的简单总结

          本文链接:https://www.haomeiwen.com/subject/jpyvhttx.html