最近项目做完了 比较空闲 在网上看了一份面试题 想自己整理一下

一.为什么说Objective-C是一门动态的语言?NSUInteger和NSInteger 的区别?

静态 动态是相对的,这里的动态语言指的是不需要在编译时确定所有的东西,在运行的时候还可以动态的添加变量 方法 和 类

OC可以通过RunTime这个运行时机制,在运行的时候动态的添加变量,方法和类等,所以OC是一门动态的语言。

OC是C语言的超集,在C语言的基础上添加了面向对象的特性,并且利用RunTime运行时机制,为OC添加了动态的特性。

C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序是如何运行的。而OC是动态语言,它并非通过调用类的方法来执行功能,而是通过给对象发送消息,对象在接收到消息之后会去找匹配的方法来运行。由于这个机制,在你调用一个方法时,它的最终实现是可以改变的,而且你可以在运行时给类添加方法。

因为在运行时可以向类中添加方法,所以编译器在编译的时候,还无法确定类中是否有某个方法的视线,对于类无法处理一个消息时,就会触发消息转发机制

1 动态方法解析,先征询接受者所属的类,能够动态添加方法,来处理这个消息,若可以则结束,如不能则继续向下走。

2 完整的消息转发机制

<1>请接收者看看有没其他对象能处理这条消息,若有,就把这个消息转发给那个对象然后结束

<2>运行时系统会把与消息有关细节全部封装到NSInvocation 对象中,再给对象最后一次机会,令其设法解决当前还未处理的这条消

int和NSInteger其实是差不多的,但更推荐使用NSInteger,因为使用NSInteger,这样就不用考虑设备是32位还是64位了  NSUInteger是无符号的,即没有负数,NSInteger是有符号的,所以NSUInteger类型不能给它赋负值

二 .讲一下MVC和MVVM,MVP?

做客户端开发,前端开发对MVC,MVP,MVVM,这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式。

在开发应用程序的时候,以求更好的管理应用程序的复杂性,基于职责分离的思想都会对应用程序进行分层。在开发图形界面应用程序的时候,会把管理用户界面的层次称为View,应用程序的数据为Model(注意这里的model指的是Domain Model 这个应用程序对需要解决的问题的数据抽象,不包含应用的状态,可以简单的理解为对象。)Model层对应用程序的业务逻辑无知,只保存数据结构和提供数据操作的接口。

有了View和Model的分层,那么就有了两个问题

<1>响应用户操作的业务逻辑(例如:排序)的管理

<2>View如何同步Model的变更

带着这两个问题开始探索MV*模式,会发现这些模式之间的差异可以归纳为对这两个问题处理的方式的不同

MVC的依赖关系

Controller和View都依赖Model层,Controller和View可以相互依赖。在一些网上的资料中Controller和View之间的依赖关系可能不一样,有些是单向依赖,有些是双向依赖。这个关系不大,后面会看到她们的依赖关系都是为了把处理用户行为触发的业务逻辑的处理权交给Controller。

MVC的调用关系

用户对View进行操作后,View捕捉到这个操作,会把处理权移交给Controller(Pass calls),Controller接着回执行相关的业务逻辑,这些业务逻辑可能需要对Model进行操作;当Model变更了以后,会通过观察者模式,通知View;View通过观察者模式收到Model的变更消息以后,会向Model请求最新的数据,重新更新视图

看似没什么特别的地方,但是有几个需要特别关注的点

1.View 把控制权交给Controller 自己不执行业务逻辑

2.Controller 执行业务逻辑并且操作Model,但是不会直接操作View,可以说它是对View无知的。

3.View和Model的同步消息是通过观察者模式实现的,而同步操作是由View自己请求Model的数据然后对视图进行更新。

需要特别注意的是MVC模式的精髓在于第三点;Model的更新是通过观察者模式告诉View的,具体的表现形式可以是Pub(发布者)/Sub(订阅者)或者是触发Events。通过观察者模式的好处就是;不同的MVC三角关系可能会有共同的Model,一个MVC三角中的Controller操作了Model以后,两个MVC中的View都会收到通知,做出改变。保持了依赖同一块Model的不同View显示数据的实时性和准确性。

MVC的优缺点

优点

1、把业务逻辑全部分离到Controller中,模块化程度高。当业务逻辑变更的时候,不需要变更View和Model,只需要Controller换成另外一个Controller就行了

2、观察者模式可以做到多视图同时更新。

缺点

1.Controller测试困难,因为视图同步操作是由View自己执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,Controller业务逻辑的正确性是无法验证的:Controller更新Model的时候,无法对View的更新操作进行断言。

2.View无法组件化。View是强依赖特定的Model的,如果需要把这个View抽出来作为一个另外一个应用程序可复用的组件就困难了。因为不同程序的的Domain Model是不一样的

MVP模式

MVP模式是MVC模式的改良,下图是MVP的依赖关系

MVP模式把MVC中的Controller变成了Presenter,可以看出,打破了View原来对于Model的依赖,其余的依赖关系和MVC模式一致。

MVP的调用关系

和MVC模式一样,用户对View的操作都会从View移交给Presenter,Persenter同样会执行相应的业务逻辑,并且对Model进行相应的操作,但是Model通过观察者模式传递给Presenter而不是View。Presenter获取到Model变更的消息以后,通过View提供的接口更新界面。

关键点

1.View不在负责同步逻辑,而是由Presenter负责,Presenter中既有业务逻辑也有同步逻辑。

2.View需要提供借口给Presenter使用。(关键)

对比在MVC中,Controller是不能操作View的,View也没有提供相应的接口;而在MVP当中,Presenter可以操作
View,View需要提供一组对界面操作的接口给Presenter进行调用;Model仍然通过事件广播自己的变更,但由Presenter监听而不是View。
 
MVP的优缺点
优点

1、便于测试。Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter业务逻辑的正确性。

2、View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务逻辑完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做高度可复用的View组件。

缺点

1、Presenter中除了业务逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。

MVVM模式

MVVM可以看作是一种特殊的MVP模式,或者说是对MVP模式的改良

ViewModel

MVVM代表的是Model-View-ViewModel,这里解释一下什么是ViewModel,ViewModel的含义就是Model of View  视图的模型。她的含义包含了领域模型(Domain Model)和视图的状态(state)。在图形界面应用程序中,界面所提供的信息可能不仅仅包含应用程序的领域模型,还可能包含一些领域模型不包含的视图状态,例如电子表格程序需要显示当前的排序的状态是顺序的还是逆序的,而这是Domain Model所不包含的,但也是需要显示的信息。

可以简单的把View Model理解为页面上所显示内容的数据抽象,和Domain Model不一样,ViewModel更适合用来描述View。

MVVM的依赖

MVVM的依赖和MVP的依赖相似 只不过是把P换成了VM

MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。以前全部由Presenter负责的View和Model之间数据同步操作交由给Binder处理。你只需要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操作(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染
也就是说,MVVM把View和Model的同步逻辑自动化了。以前Presenter负责的View和Model同步不再手动地进行操作,而是交由框架所提供的Binder进行负责。只需要告诉Binder,View显示的数据对应的是Model哪一部分即可

MVVM的优缺点

优点

1、提高可维护性。解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性。

2、简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。

缺点

1、过于简单的图形界面不适用,或说牛刀杀鸡。

2、对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。

3、数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的。

三 为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

weak 其实是一个hash(哈希)表 key是所指对象的地址,Value是weak指针的地址(当weak指针所指的对象销毁的时候,这个表根据这个对象地址(key),找到所有的只想他的Value(weak指针),并把这些指针置nil),我们都知道weak的作用:所引用的对象的计数器不会加一,并在引用对象被释放的时候自动置为nil。通常用于解决循环引用.

weak 实现原理概括

RunTime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

weak的实现原理可以概括为三步

(1)初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

(2)添加引用时 objc_initWeak函数会调用objc_storeWeak()函数,objc_storeWeak()的作用时更新指针的指向。创建对应的弱引用表

(3)释放时,调用clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

代理使用weak指针,其实就是为了解决循环引用的问题,假如一个A对象引用了另一个B对象并遵循了它的代理 如果不使用weak 就会造成 A引用了B B持有delegate delegate持有A 造城循环引用。而如果食用了weak delegate就不会持有A 打破循环引用,释放内存.

在代理中经常会看到DataSource和Delegate

DataSource 偏重于数据的回调,View里有什么东西 属性都是什么。例如UITableviewDatasource; delegate偏重于与用户交互的回调,有那些方法可以供我使用,例如UITableviewDelegate

Block和代理的区别

block和代理是iOS开发中实现回调的两种方式,大多数情况下是用哪个都可以 具体可以参考这里 https://www.jianshu.com/p/6bba9b4a25d5

四 属性的实质是什么?包括哪几个部分?属性默认的关键字有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

属性的本质也就是说@property的本质是什么

其实就是 @property = ivar + getter + setter (实例变量 + get方法 + set方法) https://www.jianshu.com/p/7ddefcfba3cb

atomic

原子操作(原子性是指事务的一个完整操作,操作成功就提交,反之就回滚。原子操作就是指具有原子性的操作),在OC属性设置中,默认的就是atomic,意思就是Setter/getter函数是一个原子操作,如果多线程同时调用setter时,不会出现某一个线程完成setter所有的语句之前,另一个线程就开始执行setter。相当于函数加了锁。这样的话,并发访问性能会比较低.

nonatomic

非原子操作属性 一般不需要多线程支持的时候就用它,这样在并发访问的时候效率会比较高,在OC中通常对象类型都应该声明为非原子性的。iOS中程序启动时系统会自动生成一个单一的主线程。程序在执行的时候一般情况下都是在同一个线程里面进行操作。如果在程序中 我们确定某一个属性会在多线程中被使用,并且需要做数据同步,就必须设置成原子性的,但也可以设置成非原子性的,然后自己在程序中用加锁之类的来做数据同步

@synthesize 和 @dynamic

如果没有实现setter和getter方法,编译将会自动生成setter和getter 如果@synthesize和@dynamic都没写 默认时@synthesize

@dynamic 属性的setter和getter方法由用户自己实现,不能自动生成 (当然对于 readonly 的属性只需提供 getter 即可)

readwrite

如果没有声明成 readonly ,那就 默认是 readwrite 。可以用来赋值,也可以被赋值

readonly

不可以被赋值 只可以调用

assgin 不会使引用计数加1 直接饮用对象 对象释放 会指向一个空地址 不安全

weak   见上面

retain strong

在ARC打开的情况下 才使用strong 引用计数加1

copy 建立一个索引计数为1的对象,在赋值时会使用传入值的一份拷贝.

综上所述,如果在声明属性的时候 没写关键字 如果是基本对象会默认 assign readwrite atomic OC对象默认  strong readwrite atomic

五 NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)

@interface ViewController ()

@property (nonatomic,strong) NSString *strongStr;
@property (nonatomic,copy) NSString * copyedStr; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; NSString *string = [NSString stringWithFormat:@"abc"];
self.strongStr = string;
self.copyedStr = string;
NSLog(@"origin string: %p, %p", string, &string);
NSLog(@"strong string: %p, %p", _strongStr, &_strongStr);
NSLog(@"copy string: %p, %p", _copyedStr, &_copyedStr); } -- ::55.804861+ StrCopyAndStrong[:] origin string: 0xa000000006362613, 0x7fff558b0c68
-- ::55.805013+ StrCopyAndStrong[:] strong string: 0xa000000006362613, 0x7f8ecce058a0
-- ::55.805123+ StrCopyAndStrong[:] copy string: 0xa000000006362613, 0x7f8ecce058a8

我们可以看到 这种情况下 不管是Strong还是copy属性的对象,其指向的地址都是同一个,即为string指向的地址。如果我们换作MRC环境,打印string的引用计数的话,会看到其引用计数值是3,即strong操作和copy操作都使原字符串对象的引用计数值加了1。

接下来 我们把string由不可变改为可变对象

NSString *string = [NSString stringWithFormat:@"abc"];

变为

NSMutableString *string = [NSMutableString stringWithString:@"abc"];

打印结果变为

-- ::04.404233+ StrCopyAndStrong[:] origin string: 0x60400005bff0, 0x7fff557d2c68
-- ::04.404412+ StrCopyAndStrong[:] strong string: 0x60400005bff0, 0x7fc8d3c08ac0
-- ::04.404522+ StrCopyAndStrong[:] copy string: 0xa000000006362613, 0x7fc8d3c08ac8

此时 可以看到copy属性的字符串已经不在指向string字符串对象,而是深拷贝了string字符串,并让_copyedStr指向这个字符串。

我们在声明一个NSString属性时,对于其内存相关特性,通常会有两种选择(基于ARC环境):strong和copy。在MRC环境下,打印两者的引用计数,可以看到string对象的引用计数是2,而_copyedString对象的引用计数是1。

此时,我们如果去修改string字符串的话,可以看到:因为_strongStr与string是指向同一对象,所以_strongStr的值也会跟随着改变(需要注意的是,此时_strongStr的类型实际上是NSMutableString,而不是NSString);而_copyedStr是指向另一个对象的,所以并不会改变。

由于NSMutableString是NSString的子类,所以一个NSString指针可以指向NSMutableString对象,让我们的strongString指针指向一个可变字符串是OK的。

而上面的例子可以看出,当源字符串是NSString时,由于字符串是不可变的,所以,不管是strong还是copy属性的对象,都是指向源对象,copy操作只是做了次浅拷贝。

当源字符串是NSMutableString时,strong属性只是增加了源字符串的引用计数,而copy属性则是对源字符串做了次深拷贝,产生一个新的对象,且copy属性对象指向这个新的对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。

这里还有一个性能问题,即在源字符串是NSMutableString,strong是单纯的增加对象的引用计数,而copy操作是执行了一次深拷贝,所以性能上会有所差异。而如果源字符串是NSString时,则没有这个问题。

所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。http://www.cocoachina.com/ios/20150512/11805.html

六 如何令自己所写的对象具有拷贝功能

若想令自己所写的对象具有拷贝功能,则需要实现NSCopying协议。如果自定义的对象氛围可变版本和不可变版本,那么需要同时实现NSCopying/NSMutableCopying协议。

具体步骤:

需要声明该类遵守NSCopying协议

实现NSCopying协议,该协议只有一个方法

- (id)copyWithZone:(NSZone *)zone;

注意:一提到让自己的类用 copy 修饰符,我们总是想覆写copy方法,其实真正需要实现的却是 “copyWithZone” 方法。

至于如何重写带copy关键字的setter方法这个问题

- (void)setName:(NSString *)name {
//[_name release];
_name = [name copy];
}

另外对于实现了NSCopying和NSMutableCopying协议的类来说,对他们的对象发送 copy 和 mutableCopy 也会有不同的效果.

copy 对于不可变对象 是浅拷贝(拷贝对象的指针,指向的是同一块内存地址<内存地址中的内容没被拷贝>) 对可变对象进行copy操作 是深拷贝(拷贝出来一个新的对象指向一个拷贝出来的新的内存地址<原内存地址中的内容也被拷贝>)  但是拷贝出来的都是不可变对象

需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制

mutableCopy 拷贝出来的都是可变对象 而且进行的是深拷贝 这篇文章讲的很具体 https://www.jianshu.com/p/e98e6e30ebda

七 为什么IBOutlet修饰的UIView也适用weak关键字

因为当我们将控件拖到Storyboard上,相当于新创建了一个对象,而这个对象是加到视图控制器的view上,view有一个subViews属性,这个属性是一个数组,里面是这个view的所有子view,而我们加的控件就位于这个数组中,那么说明,实际上我们的控件对象是属于view的,也就是说view对加到它上面的控件是强引用。当我们使用Outlet属性的时候,我们是在viewController里面使用,而这个Outlet属性是有view来进行强引用的,我们在viewController里面仅仅是对其使用,并没有必要拥有它,所以是weak的。

如果将weak改为strong,也是没有问题的,并不会造成强引用循环。当viewController的指针指向其他对象或者为nil,这个viewController销毁,那么对控件就少了一个强引用指针。然后它的view也随之销毁,那么subViews也不存在了,那么控件就又少了一个强引用指针,如果没有其他强引用,那么这个控件也会随之销毁。

不过,既然没有必将Outlet属性设置为strong,那么用weak就好了

八 nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?

在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不需要同步锁。

具备atomic特质的获取方法会通过锁定机制来确保其操作的原子性。也就是说,如果两个线程同时读取一个属性,那么不论何时,总能看到有效的属性值。

如果不加锁的话(或者说使用nonatomic语义),那么当其中一个线程正在改写某属性值的时候,另外一个线程也许会突然闯入,把尚未修改好的属性值读取出来。发证这种情况时,线程读取道德属性值肯能不对。

一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:
在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。

例如:一个线程在连续多次读取某个属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读取到不同的属性值。

因此,iOS程序一般都会使用nonatomic属性。但是在Mac OS X程序时, 使用atomic属性通常都不会有性能瓶颈;

然而atomic一定是线程安全的么,回答是NO :

nonatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全,而atomic的操作是原子性的,但并不意味着他就是线程安全的,它会增加正确的几率,能够更好的避免线程错误,但仍旧是不安全的。

为了说atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同:

nonatomic的实现:

- (void)setCurrentImage:(UIImage *)currentImage
{
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain]; // do something
}
} - (UIImage *)currentImage
{
return _currentImage;
}

atomic的实现:

- (void)setCurrentImage:(UIImage *)currentImage
{
@synchronized(self) {
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain]; // do something
}
}
} - (UIImage *)currentImage
{
@synchronized(self) {
return _currentImage;
}
}

当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为等该操作而等待。当A线程的写操作结束后,B线程进行写操作,所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。如果有线程C在A线程读操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。

更准确的说应该是读写安全,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。

其实无论是否是原子性的只是针对于getter和setter而言,比如用atomic去操作一个NSMutableArray ,如果一个线程循环读数据,一个线程循环写数据,肯定会产生内存问题,这个就跟getter和setter就木有关系了。

至于保证线程安全 我觉得可以看看这篇文章:http://www.cocoachina.com/ios/20160707/16957.html

九 UICollectionView自定义layout如何实现?

第一步 我们要自定义一个继承与UICollectionViewLayout的布局

第二步 根据需要重写UICollectionViewLayout中的一些方法 常用的方法如下

//预布局方法 所有的布局应该写在这里(在里面计算好必要的布局信息并存储起来)
open func prepare()
//返回collectionView 的尺寸
open var collectionViewContentSize: CGSize { get }
//返回rect中的所有的元素布局属性
//返回的是包含UICollectionViewLayoutAttributes的数组
open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
//根据Indexpath返回布局属性
open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
//返回头尾视图的布局属性
open func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
//返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载
open func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
//当边界发生变化时 是否重新布局 如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息
open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool

UICollectionViewLayoutAttributes的定义是这样的

@property (nonatomic) CGRect frame
@property (nonatomic) CGPoint center
@property (nonatomic) CGSize size
@property (nonatomic) CATransform3D transform3D
@property (nonatomic) CGFloat alpha
@property (nonatomic) NSInteger zIndex
@property (nonatomic, getter=isHidden) BOOL hidden
可以看到,UICollectionViewLayoutAttributes的实例中包含了诸如边框,中心点,大小,形状,透明度,层次关系和是否隐藏等信息。
1.一个cell对应一个UICollectionViewLayoutAttributes对象
2.UICollectionViewLayoutAttributes对象决定了cell的摆设位置(frame)
 
上面方法的调用顺序
:-(void)prepareLayout  设置layout的结构和初始需要的参数等
:-(CGSize) collectionViewContentSize 确定collectionView的所有内容的尺寸。
:-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect初始的layout的外观将由该方法返回的UICollectionViewLayoutAttributes来决定。
:在需要更新layout时,需要给当前layout发送
)-invalidateLayout, 该消息会立即返回,并且预约在下一个loop的时候刷新当前layout
)-prepareLayout,
)依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局

所以自定义UICOllectionViewLayout的步骤可以分为三步

1.覆写prepareLayout方法,并在里面事先就计算好必要的布局信息并存储起来。

2.基于prepareLayout方法中的布局信息,使用collectionViewContentSize方法返回UICollectionView的内容尺寸。

3.使用layoutAttributesForElementsInRect:方法返回指定区域cell、Supplementary View和Decoration View的布局属性。

10 用StoryBoard开发界面有什么弊端? 如何避免?

难以维护

Storyboard在某些角度上,是难以维护的。我所遇到过的实际情况是,公司一个项目的2.0版本,设计师希望替换原有字体。然而原来项目的每一个Label都是采用Storyboard来定义字体的,因此替换新字体需要在Storyboard中更改每一个Label。

幸亏我们知道Storyboard的源文件是XML,最终写了一个读取-解析-替换脚本来搞定这件事

性能瓶颈

当项目达到一定的规模,即使是高性能的MacBook Pro,在打开Storyboard是也会有3-5秒的读取时间。无论是只有几个Scene的小东西,还是几十个Scene的庞然大物,都无法避免。Scene越多的文件,打开速度越慢(从另一个方面说明了分割大故事板的重要性)。

让人沮丧的是,这个造成卡顿的项目规模并不是太难达到。 
我猜想是由于每一次打开都需要进行I/O操作造成的,Apple对这一块的缓存优化没有做到位。可能是由于Storyboard占用了太多内存,难以在内存中进行缓存。Whatever,这个问题总是让人困扰的。 
然而需要指出的是,采用Storyboard开发或采用纯代码开发的App,在真机的运行效率上,并没有太大的区别

错误定位难

Storyboard的初学者应该对此深有体会。排除BAD_EXCUSE错误不说,单单是有提示的错误,就足以让人在代码和Storyboard之间来回摸索,却无法找到解决方案。

一个典型的例子是,在代码中删除了IBOUTLET属性或者IBAction方法,但是却忘了在Storyboard中删除对应的连接,运行后crash。然而控制台只会输出一些模糊其词的错误描述。

*** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key drawButton.'

最后一方面是其提供的便利,另一方面是Apple对Storyboard的大力支持。这一点宏观上看,可以在以往对Storyboard的改进和增强上看出,微观上看,几乎所有iOS 8之后的simple code都或多或少采用了Storyboard作为界面开发工具;

那改如何避免这些弊端呢, 参考以下文章: 
iOS项目开发实战——storyboard设置界面技巧与注意事项

11 进程和线程的区别? 同步异步的区别?串行和并发的区别?

进程和线程

进程和线程的简单类比

进程中所包含的一个或多个执行单元称为线程。比如一个应用程序就是一个进程,而它又包含至少一个线程。

主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程;

同步 和 异步

所谓同步 就是 在线程中一个任务没有执行完毕后 下一个任务就不会执行(任务是一个接一个的执行的) 比如主线程中的任务 或者GCD中异步函数+串行队列。同步函数+并发队列

异步 和同步相对 调用异步函数执行任务 在多线程下 多个任务是可以同时执行的(并不是严格上的同时) 只是在一个任务还没结束的时候,可以开启下一个任务.

由此 可以看出 在iOS中 同步 和 异步 影响的是任务执行的方式

串行 和 并发

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的

强调同时处理多个任务的能力

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。

强调交替做不同事的能力

12 线程间的通信

线程间通信的体现

1>一个线程传递数据给另一个线程

2>在一个线程中执行完特定任务后 转到另一个线程继续执行任务

线程间通信常用的方法

1.NSthread 可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

GCD 是可以嵌套使用的 他们子线程切换到主线程的方法很简单

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{

        NSLog(@"donwload---%@", [NSThread currentThread]);

        // 1.子线程下载图片
NSURL *url = [NSURL URLWithString:@"http://d.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; // 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"setting---%@ %@", [NSThread currentThread], image); [self.button setImage:image forState:UIControlStateNormal];
});
});
}

NSOperation 转换线程的方法

- (void)downLoadImage {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"&&&&&"]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//主线程更新UI
[NSOperationQueue.mainQueue addOperationWithBlock:^{
self.imageView.image = image;
}]; }];
[queue addOperation:op1];
}

也可以移到其他线程 GCD  和 NSOperation可以传递一个自己建立的队列 不用拘泥于传递主队列(当然是在有这些需求的时候)

13 objc使用什么机制管理对象内存(内存管理方式有哪些)

内存管理的一些面试题

14 属性和成员变量的区别

成员变量的作用范围

@public: 在任何地方都能直接访问对象的成员变量

@private: 只能在当前类的对象方法中直接访问,如果子类需要访问需要通过getter/setter方法来访问。

@protected: 可以在当前类及其子类对象方法中直接访问(系统默认下就是用它来修饰的)

@package:在同一个文件包下就可以直接访问,比如同一个框架。

值得注意的是无论父类是在@interface还是@implementation声明的成员变量子类都能拥有;但是子类能不能直接通过变量名来访问父类中定义的成员变量是需要看父类中定义的成员变量是由什么修饰符来修饰的

//马甲包

https://www.jianshu.com/p/04b63de8ae23

最新文章

  1. vector it-&gt;和*it
  2. 将Jquery序列化后的表单值转换成Json
  3. SQL Server 2012 学习笔记4
  4. javascript拾掇
  5. LeetCode25 Reverse Nodes in k-Group
  6. 【原创】Tomcat集群环境下对session进行外部缓存的方法(2)
  7. DTCMS更改图片相册上传图片类型,手机上传图片相册
  8. nyoj 84阶乘后0的个数
  9. MFC下对串口的操作以及定时器的调用
  10. 【转】安卓布局:layout_weight的理解
  11. JavaScript设计模式(3)-工厂模式
  12. 微信js-sdk开发获取签名和获取地理位置接口示例
  13. ngx-moment汉化
  14. cf里的一些简单组合数题
  15. Gnome3 安装系统监视器
  16. 在swift中使用线程休眠
  17. 转-java编译时error: illegal character &#39;\ufeff&#39; 的解决办法-https://blog.csdn.net/t518vs20s/article/details/80833061
  18. AngularJS 的常用特性(五)
  19. 【openjudge】【字符串+模拟】1777:文件结构“图”
  20. js事件处理-整理

热门文章

  1. 非等高cell实战--实现微博页面
  2. http协议---简述
  3. SourceTree代码管理学习git命令操作
  4. Mysql 常用函数集
  5. atitit.复合变量,也就是类似$$a的变量的原理与实现&#160;java&#160;c#.net&#160;php&#160;js
  6. 在Jboss中使用Quartz
  7. ROW模式的SQL无法正常同步的问题总结
  8. jvm参数设置和性能调优
  9. FastMethod和PropertyUtils两种反射方法的性能比较
  10. Java Stream 官方文档翻译