IOS基础理论知识复习(二)

       在看到大牛的知识点,发现很多知识点自己一知半解,能做项目但理论不够扎实,默默地去百度总结一下.放到这里和大家交流交流.

如有侵权,告知即删!

一.基础篇

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

实质包含部分:

@property = ivar + getter + setter;

实例变量+ get方法+ set方法,也就是说使用@property系统会自动生成setter和getter方法;

默认常用关键字:

property中我们经常使用的关键字有strong,weak,assign,copy,nonatomic,atomic....等


@dynamic这个关键词,通常是用不到的。

它与@synthesize的区别在于:

使用@synthesize编译器会确实的产生getter和setter方法,而@dynamic仅仅是告诉编译器这两个方法在运行期会有的,无需产生警告。

假设有这么个场景,B类,C类分别继承A类,A类实现某个协议(@protocol),协议中某个属性( somePropety )我不想在A中实现,而在B类,C类中分别实现。如果A中不写任何代码,编译器就会给出警告:

“use @synthesize, @dynamic or provide a method implementation"

这时你给用@dynamic somePropety; 编译器就不会警告,同时也不会产生任何默认代码。

@dynamic 就是要来告诉编译器,代码中用@dynamic修饰的属性,其getter和setter方法会在程序运行的时候或者用其他方式动态绑定,以便让编译器通过编译。其主要的作用就是用在NSManageObject对象的属性声明上,由于此类对象的属性一般是从Core Data的属性中生成的,Core Data框架会在程序运行的时候为此类属性生成getter和Setter方法。


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

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

如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

copy相当于生成一个新的属性,占用新的内存空间,strong相当于一个指针,指向原来的内存空间

PS: 平时自己做项目时,大部分都是用的 strong, ARC中在效率上来讲:用copy都会判断一下是否为不可变.而strong就不用判断.

block一般使用copy关键之进行修饰,block使用copy是从MRC遗留下来的“传统”,在MRC中,方法内容的block是在栈区的,使用copy可以把它放到堆区。但在ARC中写不写都行:编译器自动对block进行了copy操作。


03.可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?

非集合类(NSString,NSNumber)

[immutableObject copy]//浅复制

[immutableObject mutableCopy]//深复制

[mutableObject copy]//深复制

[mutableObject mutableCopy]//深复制

结论:不可变进行copy是浅复制,mutableCopy是深复制,可变的copy,mutableCopy都是深复制

集合类(NSArray,NSDictionary, NSSet)

[immutableObject copy]//浅复制

[immutableObject mutableCopy]//单层深复制

[mutableObject copy]//单层深复制

[mutableObject mutableCopy]//单层深复制

结论:不可变进行copy是浅复制,mutableCopy是 单层 深复制,可变的copy,mutableCopy都是 单层 深复制


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

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

具体步骤:

copy需要实现NSCopying协议,然后实现以下方法,否则copy会crash

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

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

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

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

CopyObject  *copy= [[[self class] alloc] init];

copy.name = self.name;

copy.mobile = self.mobile;

copy.company = self.company;

copy.descInfo = self.descInfo;

return copy;

}

mutableCopy时,需要实现NSMutableCopying协议,否则mutableCopy会crash

-(id)mutableCopyWithZone:(NSZone*)zone { 

MutableCopyObject  *mutableCopy = [[[selfclass] alloc] init]; 

mutableCopy.name =self.name; 

mutableCopy.mobile =self.mobile; 

mutableCopy.company =self.company; 

mutableCopy.descInfo =self.descInfo;

return mutableCopy;

}


05.为什么IBOutlet修饰的UIView也适用weak关键字?

使用storyboard创建的viewController,那么会有一个叫 _topLevelObjectsToKeepAliveFromStoryboard的私有数组强引用所有top level的对象,同时top level对象强引用所有子对象,那么vc没必要再强引用top level对象的子对象。

个人理解: 就是storyboard强引用了一次.VC里面属性就不需要再引用一次了.


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

具备atomic特质的获取方法会通过锁定机制来确保其操作的原子性。

也就是说,如果两个线程同时读取一个属性,那么不论何时,总能看到有效的属性值。

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

相信大家都遇到过上述那种情况吧。。。。

一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:

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

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

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

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

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

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

解决方式:

手动使用互斥锁

@synchronized(锁对象) { //需要锁定的代码}

注意:锁定1份代码只用1把锁,用多把锁是无效的

互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题

缺点:需要消耗大量的CPU资源

OS开发的建议

所有属性都声明为nonatomic

尽量避免多线程抢夺同一块资源

尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力


07.UICollectionView自定义layout如何实现?

UICollectionViewLayout的功能为向UICollectionView提供布局信息,不仅包括cell的布局信息,也包括追加视图和装饰视图的布局信息。

实现一个自定义layout的常规做法是继承UICollectionViewLayout类,然后重载下列方法:

-(CGSize)collectionViewContentSize

返回collectionView的内容的尺寸

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

返回rect中的所有的元素的布局属性

返回的是包含UICollectionViewLayoutAttributes的NSArray

UICollectionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息,通过不同的UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollectionViewLayoutAttributes:

layoutAttributesForCellWithIndexPath:

layoutAttributesForSupplementaryViewOfKind: withIndexPath:

layoutAttributesForDecorationViewOfKind: withIndexPath:

-(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath

返回对应于indexPath的位置的cell的布局属性

-(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath

返回对应于indexPath的位置的追加视图的布局属性,如果没有追加视图可不重载

-(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath

返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载

-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

当边界发生改变时,是否应该刷新布局。如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息。

另外需要了解的是,在初始化一个UICollectionViewLayout实例后,会有一系列准备方法被自动调用,以保证layout实例的正确。

首先,-(void)prepareLayout将被调用,默认下该方法什么没做,但是在自己的子类实现中,一般在该方法中设定一些必要的layout的结构和初始需要的参数等。

之后,-(CGSize)collectionViewContentSize将被调用,以确定collection应该占据的尺寸。注意这里的尺寸不是指可视部分的尺寸,而应该是所有内容所占的尺寸。collectionView的本质是一个scrollView,因此需要这个尺寸来配置滚动行为。

接下来-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect被调用,这个没什么值得多说的。初始的layout的外观将由该方法返回的UICollectionViewLayoutAttributes来决定。

另外,在需要更新layout时,需要给当前layout发送 -invalidateLayout,该消息会立即返回,并且预约在下一个loop的时候刷新当前layout,这一点和UIView的setNeedsLayout方法十分类似。在-invalidateLayout后的下一个collectionView的刷新loop中,又会从prepareLayout开始,依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局。

你可能感兴趣的:(IOS基础理论知识复习(二))