基础知识总结(一)
最近大雨倾盆,面对雨夜,心里顿时思绪万千。思想自己工作几年,项目也写了不少,但是有一些基础的东西还是懵懵懂懂。需要的时候才会去查,去研究。有一些东西一知半解的使用,以至于给自己挖出了好多坑,埋下了好多“暗雷”。一直没有做一个系统的整理。刚好最近不是很忙,就借这个时期好好的整理一下知识点。
1.OC为什么是一门动态的语言?
这里先明确一个词「动态语言」。什么是动态语言,这个概念没有一个明确的定义。它是一个程度的度量,就是该语言的runtime到底使用多少bookkeeping的数据。Object-C语言是C语言的一个子类,都知道C语言是一个静态语言。但是Object-C三大特性之一的多态性让其拥有了动态性。简单来说,运行时机制是我们直到程序运行的时候才去决定一个对象的类别,以及调用该类别对象指定方法。
Object-C的动态特性的三个体现:动态类型、动态绑定、动态加载
(1)动态类型
简单来说就是id类型。动态类型跟静态类型是相对的。比如内置的的明确的基本类型都属于静态类型(int,NSString等)。静态类型在程序编译的时候就会被识别。所以在程序编译时发现类型不对应,Xcode会发出警告。而动态类型在程序编译时是不能被识别的,要等到运行时(runtime),才会根据语境来识别。
(2)动态绑定(dynamic binding)
动态绑定只需理解@selector/SEL即可。什么是“函数”,对于一些其他的静态语言,如C++,C,一般在编译的时候就已经将要调用的函数签名告诉编译器了。静态的,不可改变。在Object-C中,其实是没有函数的概念的。函数方法的调用是通过“消息机制”来实现的。调用一个实例的方法,所做的是向该实例的指针发送消息。实例在接收到消息之后,从自身的实现中寻找响应这条消息的方法。Object-C可以先跳过编译,到运行时在决定再调用什么方法,需要传入什么参数,这就是动态绑定。实现的前提是必须使用SEL变量去绑定一个方法。最终形成的这个SEL变量就是代表一个方法的引用。
注:SEL并不是C里面的函数指针,SEL只是一个整数,代表它绑定的那个方法的编号ID。@selector()就是获取类方法的编号。
(3)动态加载
根据需求加载所需要的资源。对于开发者来说,需要对不同的机型进行适配。如:在Retina设备上的加载的是@2x的图片,在一些老一些的普通屏上加载原图。
这里有一些有关于runtime的简单使用。
2.属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?
属性默认的关键字:基本数据:atomic,readwrite,assign
普通的OC对象:atomic,readwrite,strong
(1)@property
公式表达:ivar + getter + setter 。 @property其实就是在编译阶段又编译器为我们自动生成ivar成员变量的getter和setter方法。我们在声明一个属性的时候,编译器都做了什么:没次增加一个属性,系统都会在ivar_list中添加一个成员变量的描述。在method_list中添加setter和getter方法的描述。在prop_list中增加一个属性的描述,计算该属性在对象中的偏移量,然后给出setter和getter方法对应的实现。在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值。为了能够读取正确的字节数,系统对象偏移量的指针类型进行了类型强转。
(2)关键字
readwrite:属性可读可写(属性声明的时候,系统默认)
readonly:属性只可以读取,不可再次赋值,可以获取
assign:不会使引用计数+1,直接赋值
retain:会使引用计数+1
copy:建立一个索引计数为1的对象,在赋值的时使用传入值的一份拷贝
nonatomic:非原子性访问,多线程并发访问会提高性能
atomic:原子性访问
strong:ARC模式下会使用,相当于retain
weak:ARC模式下会使用,相当于assign,可以把对应的指针变量置为nil的状态。
(3)@synthesis和@dynamic的作用
@property有两个对应的词,一个是@synthesis一个是@dynamic。若两者都没有写,默认的就是:
@syntheszie var = _var;
@synthesis的语义是如果你没有手动实现setter和getter方法,那么编译器会自动为你加上这两个方法
@dynamic告诉编译器:属性的setter和getter方法由开发者自己实现,不需要自动生成。(对于readonly的属性只需提供getter即可)加入一个属性被声明为
@dynamic var;
然后你也没有提供@setter和@getter方法,可以通过编译,但是运行的时候,程序运行至
instance.var = someVar
会因为缺少setter方法而crash。同样如果缺失getter方法,程序运行至
someVar = instance.var
时导致crash。
3.NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)
因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属相不受外界的影响。使用copy无论给我传入的是一个可变的对象或者是一个不可变对象,我本身持有的就是一个不可变的副本。
若使用strong,那么这个属性就有可能指向一个可变对象。如果这个可变对象在外部修改了,那么会影响该属性。
4.为什么代理要用weak?week 和 assign 的区别?block和代理的区别?
(1)weak修饰代理属性,指明该对象并不负责保持delegate这个对象。delegate这个对象的销毁又外部控制
(2)weak和assign的不同点:weak策略在属性所指的对象遭到销毁时,系统会将weak修饰的属相对象的指针置为nil,在OC给nil发消息是不会有什么问题的。如果是用assign,在属性所指的对象销毁之后,属性对象的指针并为置为nil,造成野指针的存在。这个时候再次给此对象发送消息,极易造成崩溃问题。assign可以用于修饰非OC对象,而weak必须勇于OC对象。
(3)block和代理的区别:
block更轻型,是用也更简单,能够直接访问上下文,类中不需要存储临时数据,使用block,代码连贯,易读。delegate需要实现接口,与方法实现进行分离,需要存储一些临时数据。代码比较分离,不易读。但是可以在很大程度上实现代码的解耦。
block根据使用分类,有三种情形:
a.临时性的,只用在栈中,不会存储起来
b.需要存储起来,但只会调用一次,或者一个完成时期。比如一个UIView动画,动画完成之后,需要使用block通知外面。一旦调用block之后,这个block就可以删除掉
c.需要存储起来,可能会调用多次。比如按钮的点击事件,假如采用block实现,这种block就需要长期存储,并且会调用多次。调用之后block也会删除,可能还有下一次按钮的点击。
对于临时性的,只在栈中使用的block,没有循环引用的问题。block会自动释放掉。而只调用一次的block,就需要看内部的实现了。正确的实现时block调用之后,吗上赋值为空。这样block也会释放掉,同样也不会引起循环引用。
多次调用的block,极易引起循环引用,使用时需要注意。
5.可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?
浅复制(shallow copy):在复制的时候,对于被复制对象的每一层都是指针复制
深赋值(one-level-deepcopy):在深复制操作时,对于被复制对象,至少有一层时深复制
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制
非集合类对象的copy与mutableCopy
[不可变对象 copy] //浅复制
[不可变对象 mutableCopy] //深复制
[可变对象 copy] //深复制
[可变对象 mutableCopy] //深复制
类对象的copy与mutableCopy
[不可变对象 copy] //浅复制
[不可变对象 mutableCopy] //单层深复制
[可变对象 copy] //单层深复制
[可变对象 mutableCopy] //单层深复制
需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。
6.为什么IBOutlet修饰的UIView也适用weak关键字?
我们将控件拖到Storyboard上,相当于创建了一个对象,而这个对象是加到试图控制器的view上,存放在view的subviews数组中。即我们的控件对象是属于的view的,view对其子控件之前的关系是强引用。当我们使用Outlet属性的时候,这个Outlet属性是有view来进行强引用的。我们是在viewController中仅仅对其进行使用,没有必要拥有它,所以使用weak进行修饰。
7.nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
nonatomic和atomic用来决定编译器生成的getter和setter操作是否为原子操作。atomic不是绝对的线程安全。atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。如:
声明一个NSMutableArray的原子属性stuff,此时self.stuff 和 self.stuff = otherstuff都是线程安全的。但是使用[self.stuff objectAtIndex:index]就不是线程安全的。需要用互斥锁来保证线程安全性。
8.UICollectionView自定义layout如何实现?
9.用StoryBoard开发界面有什么弊端?如何避免?
所有的viewController都在同一个storyboard里进行编辑,随着场景的增加,Xcode打开storyboard的速度会越来越慢,所有的viewController会并列在编辑器左侧,不方便编辑。无法单独调整每个场景的生命周期,所有的场景生命周期有storyboard进行控制。一旦加载了一个场景,除非storyboard卸载,否则卸载场景。
优化:1>.针对流程结构,可以添加一个场景流程文档。
2>.纯代码开发
10.数据持久化的几个方案
FMDB、Core Data、NSUserDefaults、plist、NSKeyedArchiver(对象归解档)