iOS之OC深入理解

IOS 属性及关键词汇总

2018-10-09  本文已影响0人  天遥love
最近统一整理了一下ios中属性和关键词的一些知识点,这些问题不管是面试题还是面试官都是经常会问到的,很多人都是会用,但是被问到的时候答不上来,这就会造成一个尴尬的局面,所以这些东西还是需要我们认真理解和掌握的,这样我们才能更好的使用。
我还整理的一些其他方面的,我会陆续在简书中更新。如果有写得不对的地方和理解不到位的地方,欢迎大家指正,跟大家一块学习(欢迎评论区交流)。(整理来自一些书籍、博客等)

问题汇总目录(先看看自己能答出几道):
1.0 属性的关键字
2.0 浅拷贝和深拷贝的区别
3.0 copy和strong的区别
4.0 为什么不可变对象要用copy?
5.0 assign可以用于OC对象吗?(野指针)
6.0 weak如何实现自动赋nil
7.0 swift中如何理解copy-on-wirte?
8.0 swift中,如何在结构体、enum、extension实例方法中修改成员变量?
9.0 请你讲讲@proprety关键字的作用
10.0 这个写法会出什么问题: @property (copy) NSMutableArray *array?
11.0 @synthesize合成实例变量的规则是什么?假如property名为person,存在一个名为_ person的实例变量,那么还会自动合成新变量么?
12.0 在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?
13.0 @synthesize和@dynamic分别有什么作用?
14.0 @protocol 和 category 中如何使用 @property
15.0 __block和 __weak的区别和使用
16.0 __block的实现原理你知道吗?
17.0 block的实质是什么?有几种block?分别是怎样产生的?
18.0 代理和block的比较

1.0 属性的关键字

属性的关键字不管是在笔试还是面试的过程中经常会被问到,但很多人只是会使用,关于它的定义和使用方式的理解是不到位的,我觉得要想更好的使用这些关键字,这些是需要完全理解和掌握的。接下来我们就详细的说一下属性的关键字都有哪一些。

属性的关键字分为三类:
@property (nonatomic, strong)             NSString *str1;
@property (nonatomic,__unsafe_unretain)   NSString *str2;

执行如下的代码

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

这次根本就不用输出,在没有输出前,程序已经崩溃了,其实就是野指针造成的,为何会造成野指针呢?同于用unsafe_unretained声明的指针,由于 self.str1=nil已将内存释放掉了,但是str2并不知道已被释放了,所以是野指针。然后访问野指针的内存就造成crash. 所以尽量少用unsafe_unretained关键字。

在oc中基本数据类型的默认关键字是atomic,readwrite和assign,普通属性的默认关键字是atomic,readwrite和strong。

2.0浅拷贝和深拷贝的区别

copy和mutableCopy在使用中的总结:

如果我们让一个类对象进行复制的操作,我们需要遵守NSCopying或者NSMutableCopying协议,没有遵守就会出现异常。对于我们自定义的类,我们需要实现copyWithZone: 和mutableCopyWithZone: 方法,系统的类不需要,因为系统已经帮我们实现了。
iOS开发——深拷贝与浅拷贝详解

3.0 copy和strong的区别

4.0 为什么不可变对象要用copy

因为该对象可能指向一个可变的对象,若用strong的话,设置完属性之后,可变实例的值可能会在对象不知情的情况下遭到更改。用copy的话就会重新生成一个新的对象,新的对象不会受源对象值改变的影响。

5.0 assign可以用于OC对象吗?

assign可以修饰OC对象如NSString等类型对象,使用assign修饰不会更改所赋新值的引用计数,也不会改变旧值的引用计数,如果所赋新值的引用计数为零对象被销毁时,属性并不知道,编译器不会将其置为nil,指针仍然指向被销毁的内存,造成“野指针”,在堆上容易造成崩溃。所以一般用assign来修饰基本数据类型,基本数据类型在栈上,而栈上的内存系统会自动处理,不会造成“野指针”。

扩展

野指针:指针指向了一个已经被回收的对象。
僵尸对象: 一个已经被释放的对象。

6.0 weak如何实现自动赋nil

通过weak表。runtime维护了一个weak表,用来存储所有的weak指针。weak表其实就是一个哈希表,key代表对象的地址,value代表一个数组,数组里面存放的是weak指针的地址,此地址的值其实就是所指对象的地址。
释放时调用clearDeallocating函数,clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

问题点

1.0 从weak指针的创建到释放存在哈希表的增删改查,所以存在一定的性能开销。
2.0 使用 Weak 指针的时候,应首先获取一个 Strong 指针再使用。倒不是为了防止在使用过程中,对象被回收,形成野指针。 这个不用担心,因为你使用了 Weak 指针,对象就会被加入到 autoreleasepool 中,可以放心使用。但是要注意的是,如果在一个代码块中频繁使用 Weak 指针,还是应首先获取一个 Strong 指针,否则这个对象会被一次又一次的加入 autoreleasepool 中,也存在一定的性能开销。
详细请看--底层解析weak的实现原理

7.0 swift中如何理解copy-on-wirte?

在swift中当值类型(例如struct)在进行复制时,复制后的对象和原对象实际上在内存中指向的是同一个对象,只有当复制后的对象进行修改时,才会重新创建一个新的对象。在swift中我们平时使用的Int、Double、Dictionary、Array等都是使用结
构体(struct)实现的,我们用Array举个例子:

        // arr1是一个值类型
        let arr1 = ["tianyao", "ningfei", "tian"]
        // 此时arr2和arr1在内存中是同一个数组,并没有产生新的数组
        var arr2 = arr1
        // 此时arr2追加了一个元素,被修改,那么arr2在内存中重新产生了一个新的数组,而不是原来的arr1
        arr2.append("change")

从上面的代码我们可以看得出来,复制的数组和源数组指向的是同一个地址,只有当两者中的一方发生修改时,才会重新开辟一个新的地址。这样的设计使得值类型可以多次复制而不需要开辟新的内存,只有当一方有变化时才会开辟新的内存。这样使得内存有更高效的使用。

8.0 swift中,如何在结构体、enum、extension实例方法中修改成员变量?

在官方文档中有这么一句话:

“Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.”

大概的意思是:虽然结构体和枚举中能够定义自己的方法,但是默认情况下,实例方法中是不可以修改值类型的属性。

这个问题的关键点就是一个关键词mutating(可变化,转变)。使用mutating来修饰结构体、枚举和extension定义的方法,就可以修改成员变量了。

9.0 请你讲讲@proprety关键字的作用

10.0 这个写法会出什么问题: @property (copy) NSMutableArray *array?

11.0 @synthesize合成实例变量的规则是什么?假如property名为person,存在一个名为_ person的实例变量,那么还会自动合成新变量么?

如果使用了属性的话,那么编译器就会自动编写访问属性所需的方法,此过程叫做“自动合成”。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法” 的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。

@synthesize合成实例变量的规则如下:
@interface TYPerson : NSObject 

    @property NSString *firstName; 
@end

上面的例子会生成一个_firstName的成员变量,如果想让成员变量重新命名,也可以在类的实现方法里面通过@synthesize语法来指定成员变量的名字,如下

@implementation TYPerson 

    @synthesize firstName = _tianYao; 
@end 

此时就会生成一个_tianYao的成员变量。

假如property名为person,存在一个名为_ person的实例变量,那么还会自动合成新变量么? 答案是不会的

如果存在_person的实例变量,那么你再创建一个_person的变量,系统就会报错。

附加:成员变量 = 实例变量 = Ivar

12.0 在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?

13.0 @synthesize和@dynamic分别有什么作用?

简单的来说一个是自动的,一个是手动的。
当你定义了一个属性,使用@synthesize的时候,如果你没有手动的实现getter和setter的方法,那么编译器会自动的帮你添加这两个方法。那么使用@dynamic,你需要手动实现它的getter和setter的方法,编译器不会帮你做这件事情。假如你没有手动实现这两个方法的话,程序在编译的过程中没有问题,但当程序运行到someVar = var的时候,由于缺少getter方法,会导致系统崩溃。同样当运行到instance.var = someVar的时候,由于缺少setter方法,也会导致系统崩溃。
此过程编译时候是没有问题的,运行时才执行相应的方法,这就是动态绑定。

14.0 @protocol 和 category 中如何使用 @property?

a.在@protocol中使用@property:

在@protocol中添加@property,其实就是声明了getter和setter的方法。我们需要在实现这个协议的类中,手动添加实例变量,并实现getter和setter的方法。

b.category中使用@property:

在category中添加property时, 在@implentation添加 getter 和 setter方法时, 由于category不能添加实例变量,我们可以通过两个方法来实现:

代码请看这里

15.0 __block和 __weak的区别和使用

附加:__block和__weak的使用都跟block有关。

16.0 __block的实现原理你知道吗?

block是不允许修改外部变量的值得,这里的外部变量的值,其实就是栈中指针的内存地址,__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。

Block不允许修改外部变量的值Apple这样设计,应该是考虑到了block的特殊性,block也属于“函数”的范畴,变量进入block,实际就是已经改变了作用域。在几个作用域之间进行切换时,如果不加上这样的限制,变量的可维护性将大大降低。又比如我想在block内声明了一个与外部同名的变量,此时是允许呢还是不允许呢?只有加上了这样的限制,这样的情景才能实现。

深入研究Block捕获外部变量和__block实现原理

17.0 block的实质是什么?有几种block?分别是怎样产生的?

常见的block有三种类型:

在GC环境下还有3种使用的_NSConcreteFinalizingBlock,_NSConcreteAutoBlock,_NSConcreteWeakBlockVariable,可以看看官方文档。

附加:

GC(garbage collection):一个跟踪过程,它传递性地跟踪指向当前使用的对象的所有指针,以便找到可以引用的所有对象,然后重新使用在此跟踪过程中未找到的任何堆内存。公共语言运行库垃圾回收器还压缩使用中的内存,以缩小堆所需要的工作空间 。

18.0 代理和block的比较

注意:block容易出现循环引用。

附加:通知跟block和delegate相比,它可以一对多,也可以跨控制器进行传值,它是基于kvo来实现的。(kvo的话后续会说)

未完待续(网络、多线程、数据库等方面的我也会单独整理出来)

上一篇下一篇

猜你喜欢

热点阅读