转载

面试总结

  • 不需要编写多少代码,实现比较简单;
  • 对于一个发出的通知,多个对象能够做出反应,即1对多的方式实现简单
  • controller能够传递context对象(dictionary),context对象携带了关于发送通知的自定义的信息

notification的缺点

  • 在编译期不会检查通知是否能够被观察者正确的处理;
  • 在释放注册的对象时,需要在通知中心取消注册;
  • 在调试的时候应用的工作以及控制过程难跟踪;
  • 需要第三方对喜爱那个来管理controller与观察者对象之间的联系;
  • controller和观察者需要提前知道通知名称、UserInfo dictionary keys。如果这些没有在工作区间定义,那么会出现不同步的情况;
  • 通知发出后,controller不能从观察者获得任何的反馈信息。

KVO 的 优势 :

  • 1.能够提供一种简单的方法实现两个对象间的同步。例如:model和view之间同步;
  • 能够对非我们创建的对象,即内部对象的状态改变作出响应,而且不需要改变内部对象(SKD对象)的实现;
  • 能够提供观察的属性的最新值以及先前值;
  • 用key paths来观察属性,因此也可以观察嵌套对象;
  • 完成了对观察对象的抽象,因为不需要额外的代码来允许观察值能够被观察

KVO 的 缺点 :

  • 对属性重构将导致我们的观察代码不再可用
  • 官方接口并不好用

效率

  • 肯定是delegate比NSNotification高。

delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要关注返回值, 也就是delegate方法的结果。比如-windowShouldClose:,需要关心返回的是yes还是no。所以delegate方法往往包含 should这个很传神的词。也就是好比你做我的delegate,我会问你我想关闭窗口你愿意吗?你需要给我一个答案,我根据你的答案来决定如何做下一 步。相反的,notification最大的特色就是不关心接受者的态度, 我只管把通告放出来,你接受不接受就是你的事情,同时我也不关心结果。所以notification往往用did这个词汇,比如 NSWindowDidResizeNotification,那么NSWindow对象放出这个notification后就什么都不管了也不会等待接受者的反应。

  • delegate和NSNotification的区别 :

和delegate一样,KVO和NSNotification的作用也是类与类之间的通信,与delegate不同的是1)这两个都是负责发出通知,剩下的事情就不管了,所以没有返回值;2)delegate只是一对一,而这两个可以一对多。这两者也有各自的特点。

如何手动触发一个value的KVO

所谓的“手动触发”是区别于“自动触发”:

自动触发是指类似这种场景:在注册 KVO 之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了。

想知道如何手动触发,必须知道自动触发 KVO 的原理:

键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后, observeValueForKey:ofObject:change:context: 会被调用,继而 didChangeValueForKey: 也会被调用。如果可以手动实现这些调用,就可以实现“手动触发”了。

那么“手动触发”的使用场景是什么?一般我们只在希望能控制“回调的调用时机”时才会这么做。

具体做法如下:

如果这个 value 是 表示时间的 self.now ,那么代码如下:最后两行代码缺一不可。

相关代码已放在仓库里。

怎么用 copy 关键字?

NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

block 与 delegate 的区别

delegate运行成本低。block成本很高的。而且delegate是经典设计模式也就是大部分的语言都可以实现的模式,相对block出现比较早。block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。相对C的函数指针,只多做了一个查表动作

##

根据苹果文档对Runtime的解释

id objc_msgSend(id self, SEL _cmd, ...)
将一个消息发送给一个对象,并且返回一个值。
self是消息的接受者,
_cmd是selector,
…是可变参数列表。

消息转发和发送

load 与 initialize 的区别

load和initialize方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用。这两个方法会被自动调用,不能手动调用它们。

load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。

load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。

load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

文/bestswifter(简书作者)

原文链接: http://www.jianshu.com/p/d25f691f0b07

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

method swizzle原理

使用场景

我们想跟踪在程序中每一个view controller展示给用户的次数:当然,我们可以在每个view controller的viewDidAppear中添加跟踪代码;但是这太过麻烦,需要在每个view controller中写重复的代码。创建一个子类可能是一种实现方式,但需要同时创建UIViewController, UITableViewController, UINavigationController及其它UIKit中view controller的子类,这同样会产生许多重复的代码。

Swizzling应该总是在+load中执行

在Objective-C中,运行时会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。由于method swizzling会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争的情况。+load能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。相比之下,+initialize在其执行时不提供这种保证—事实上,如果在应用中没为给这个类发送消息,则它可能永远不会被调用。

Swizzling应该总是在dispatch_once中执行

与上面相同,因为swizzling会改变全局状态,所以我们需要在运行时采取一些预防措施。原子性就是这样一种措施,它确保代码只被执行一次,不管有多少个线程。GCD的dispatch_once可以确保这种行为,我们应该将其作为method swizzling的最佳实践。

kvo的实现

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);

SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);

Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));

if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}

isMemberOfClass 和 isKindOfClass 的区别

isKindOfClass和isMemberOfClass之间的区别是:

我们可以使用isKindOfClass来确定一个对象是否是一个类的实例,或者是该类祖先类的实例。

isMemberOfClass只能用来判断前者,不能用来判断后者。

可以说:isMemberOfClass不能检测任何的类都是基于NSObject类这一事实,而isKindOfClass可以。

iOS多线程编程——GCD与NSOperation总结

其实,如果不考虑其他任何因素和技术,多线程有百害而无一利,只能浪费时间,降低程序效率。

是的,我很清醒的写下这句话。

试想一下,一个任务由十个子任务组成。现在有两种方式完成这个任务: 1. 建十个线程,把每个子任务放在对应的线程中执行。执行完一个线程中的任务就切换到另一个线程。

  1. 把十个任务放在一个线程里,按顺序执行。

操作系统的基础知识告诉我们,线程,是执行程序最基本的单元,它有自己栈和寄存器。说得再具体一些,线程就是“一个CPU执行的一条无分叉的命令列”。

对于第一种方法,在十个线程之间来回切换,就意味着有十组栈和寄存器中的值需要不断地被备份、替换。 而对于对于第二种方法,只有一组寄存器和栈存在,显然效率完胜前者

GCD:

三种队列:

串行队列(先进入队列的任务先出队列,每次只执行一个任务)

并发队列(依然是“先入先出”,不过可以形成多个任务并发)

主队列(这是一个特殊的串行队列,而且队列中的任务一定会在主线程中执行)

两种执行方式:

同步执行

异步执行

iOS多线程编程——GCD与NSOperation总结

  1. 为什么要有 runtime?
  2. 在 .m 文件的@implementation外面定义一个 static NSArray *array?
  3. runtime 如何实现 weak 属性 ?
  4. objc中向一个nil对象发送消息将会发生什么?
  5. selector,IMP 和 Method
  6. 能否向编译后得到的类中增加实例变量?能否在运行时向类中添加实例变量(无论在MRC下还是ARC下均不需要手动释放)?能否在运行时添加实例方法

typedef struct objc_method *Method;

struct objc_method {

SEL method_name; // 方法名称

charchar *method_typesE; // 参数和返回类型的描述字串

IMP method_imp; // 方法的具体的实现的指针

}

runtime对注册的类,会进行布局,对于weak对象会放入一个hash表中,用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会dealloc,加入weak指向的对象内存地址是a,那么就以a为键,在这个weak表中搜索,找到所有以a为键的weak对象,从而设置为nil。 (网上看到的。。)

1. load和initialize的共同特点

  • 在不考虑开发者主动使用的情况下,系统最多会调用一次
  • 如果父类和子类都被调用,父类的调用一定在子类之前
  • 都是为了应用运行提前创建合适的运行环境
  • 在使用时都不要过重地依赖于这两个方法,除非真正必要

1. load和initialize的不同

  1. load方法相关要点

废话不多说,直接上要点列表:

  • 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理。
  • 补充上面一点,对于有依赖关系的两个库中,被依赖的类的load会优先调用。但在一个库之内,调用顺序是不确定的。
  • 对于一个类而言,没有load方法实现就不会调用,不会考虑对NSObject的继承。
  • 一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。
  • Category的load也会收到调用,但顺序上在主类的load调用之后。
  • 不会直接触发initialize的调用。
  1. initialize方法相关要点

同样,直接整理要点:

  • initialize的自然调用是在第一次主动使用当前类的时候(lazy,这一点和Java类的“clinit”的很像)。
  • 在initialize方法收到调用时,运行环境基本健全。
  • initialize的运行过程中是能保证线程安全的。
  • 和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要super调用。
原文  http://lastdays.cn/2016/04/21/面试问题总结/
正文到此结束
Loading...