查看原文
其他

Swift 内存管理(2.1 高级)

2017-02-05 十三 一起众创

Swift 内存管理(一)

内容列表

  1. 深浅拷贝

  2. 字符串拷贝

  3. 集合类的拷贝

  4. 局部临时对象和全局对象

  5. 类型属性的声明周期

  6. 隐式强引用 - 集合类,timer,元组

  7. 闭包属性引起的循环强引用

  8. 解决闭包属性的强引用问题

详细内容

1. 深浅拷贝

  深拷贝与浅拷贝这一概念在iOS开发中比较重要,这两个概念之前的区别在于:深拷贝是将新开辟一块空间将某一对象复制过来放进去,也就是说旧的和新的一模一样,但是是两个独立的个体。相互之间互不影响,其中一个对象改变其中的值。另一个对象并不会受到影响。

  浅拷贝是开辟一块内存空间,但是里面放的并不是新复制的对象。而是放的指向被复制对象的地址的指针(objective-c中的说法,一下均简称OC),换句话说新开辟的空间里面并没有保存新的拷贝对象,也就是说,当改变对象其中的属性时,拷贝的内容也会跟着改变。   

代码演示

  

<!--结构体。值类型-->

struct DeepCopy {

    var copy : Int = 0

}


<!--类,引用类型-->

class ShallowCopy {

    var copy : Int = 0

}


var d0 = DeepCopy(copy: 90)

var d1 = d0

d1.copy = 9

print("d0.copy : \(d0.copy)")

print("d1.copy : \(d1.copy)")


print("----分行线----")


var s0 = ShallowCopy()

s0.copy = 2

var s1 = s0

print("s0.copy: \(s0.copy)")

print("s1.copy: \(s1.copy)")

打印信息

d0.copy : 90

d1.copy : 9

----分行线----

s0.copy: 2

s1.copy: 2

从结果可以看出:值类型的赋值操作为深拷贝,引用类型的赋值操作为浅拷贝。正因为应用类型的赋值操作为浅拷贝,所以才需要内存管理  

2. 字符串拷贝

  首先针对字符串拷贝在OC中内容较多,涉及知识点颇广,这里不做扩展描述,只介绍Swift字符串的拷贝介绍,在Swift中有自己的String类型字符串,也有OC类型的NSString类型。针对不一样的类型字符串的拷贝方式不同。具体如何不同请看下面代码。   

代码演示

var swiftStr : String  = "Hello"

var swiftStr1 = swiftStr

swiftStr1 += " World"

print("swiftStr : \(swiftStr)")

print("swiftStr1 : \(swiftStr1)")


print("---分行线----")


var ocStr = NSMutableString(string: "Hello")

var ocStr1 = ocStr

ocStr1.insert(ocStr as String, at: ocStr.length)

print("ocStr : \(ocStr)")

print("ocStr1 : \(ocStr1)")

打印信息

<!--改变一个值-->

swiftStr : Hello

<!--另一个并没有变化-->

swiftStr1 : Hello World

---分行线----

<!--改变一个值-->

ocStr : HelloHello

<!--另一个跟着变化了-->

ocStr1 : HelloHello

从结果可以看出Swift的字符串String类型是深拷贝,而OC中常使用的字符NSMutabliString的类型是浅拷贝,原因究其根本的话需要点击到String和NSMutabliString里面查看一下(快捷键是 command + 鼠标左键 + 需要查看的内容)

点开后的信息

Swift中的String是结构体,值类型,根据上面的阐述,值类型都是深拷贝

使用OC中常使用的NSString类以及其子类NSMutableString,都是类,引用类型,根据上面的阐述,引用类型都是浅拷贝

3. 集合的拷贝

  对于此处结合我只介绍最简单,最粗浅的数组与字典,至于数组里面放置的内容是值类型或者是引用类型,此处先不做过深探讨,后续会有介绍,同样的此处的介绍也分为Swift与Foundation框架内的类区分介绍。

代码演示

1. 数组的拷贝

<!--Swift数组-->

var swiftArray : Array <Int> = [1,2,3]

var swiftArray1 = swiftArray

swiftArray1 += [4]

print( "swiftArray1 : \(swiftArray1)")

print("swiftArray : \(swiftArray)")


<!--OC常用数组-->

var ocArray = NSMutableArray(array: [1, 2, 3 , 4])

var ocArray1 = ocArray

ocArray1.add(5)

print("ocArray:\(ocArray)")

print("ocArray1:\(ocArray1)")

打印信息

<!--Swift的原生数组为深拷贝-->

swiftArray1 : [1, 2, 3, 4]

swiftArray : [1, 2, 3]


<!--OC中常用的数组为浅拷贝-->

ocArray:(

    1,

    2,

    3,

    4,

    5

)

ocArray1:(

    1,

    2,

    3,

    4,

    5

)

2. 字典拷贝

<!--Swift原生的字典-->

var swiftDict : Dictionary = [1 : "1" , 2 : "2"]

var swiftDict1 = swiftDict

swiftDict[3] = "3"

print("swiftDict1 :\(swiftDict1)")

print("swiftDict :\(swiftDict)")


<!--OC常使用的字典-->

var ocDict = NSMutableDictionary(dictionary: [1 : "1", 2 : "2", 3 : "3"])

var ocDict1 = ocDict

ocDict1.addEntries(from: [4 : "4"])

print("ocDict1 :\(ocDict1)")

print("ocDict : \(ocDict)")

打印信息

<!--Swift原生的字典-->

swiftDict1 :[2: "2", 1: "1"]

swiftDict :[2: "2", 3: "3", 1: "1"]


<!--OC常用的字典-->

ocDict1 :{

    3 = 3;

    2 = 2;

    1 = 1;

    4 = 4;

}

ocDict : {

    3 = 3;

    2 = 2;

    1 = 1;

    4 = 4;

}

查看类型实现细节

1. Swift数组

1.1 OC数组

2. Swift字典

2.1 OC字典

从上可以看出Foundation类库类面的类对象在Swift中全都被重写成引用类型的,而Swift原生的数组以及字典都是值类型

4. 深入分析集合类的拷贝

  这里深入介绍一下数组的拷贝情况,之前介绍的数组拷贝都是很简单的拷贝,没有讨论数组里面的元素是深拷贝对象,还是浅拷贝对象。这里需要演示的就是这类情况。

  

代码演示

引用之前的代码片段

//结构体。值类型

struct DeepCopy {

    var copy : Int = 0

}


//类,引用类型

class ShallowCopy {

    var copy : Int = 0

}

数组拷贝演示

<!--深拷贝对象-->

var de0 = DeepCopy()

var de1 = DeepCopy()

<!--数组元素是深拷贝对象-->

var deArray = [de0, de1]


<!--浅拷贝对象-->

var sh0 = ShallowCopy()

var sh1 = ShallowCopy()

<!--数组元素是浅拷贝对象-->

var shArray = [sh0, sh1]

<!--拷贝操作-->

var deArray1 = deArray

var shArray1 = shArray


//修改数组里面的元素的值

deArray1[0] = DeepCopy(copy: 3)

print("deArray[0].copy : \(deArray[0].copy)")

print("deArray1[0].copy : \(deArray1[0].copy)")


<!--修改数组里面的元素的值-->

shArray[0].copy = 2

print("shArray[0].copy : \(shArray[0].copy)")

print("shArray1[0].copy : \(shArray1[0].copy)")


<!--删除深拷贝元素的数组元素-->

deArray1.remove(at: 1)

print("deArray1.count : \(deArray1.count)")

print("deArray.count : \(deArray.count)")


<!--删除浅拷贝元素的数组元素-->

shArray1.remove(at: 0)

print("shArray1.count : \(shArray1.count)")

print("shArray.count : \(shArray.count)")

打印信息

<!--第一部分-->

//修改数组里面的元素的值

deArray[0].copy : 0

deArray1[0].copy : 3


<!--修改数组里面的元素的值-->

shArray[0].copy : 2

shArray1[0].copy : 2


<!--第二部分-->

<!--删除深拷贝元素的数组元素-->

deArray1.count : 1

deArray.count : 2


<!--删除浅拷贝元素的数组元素-->

shArray1.count : 1

shArray.count : 2

  分析上面的打印信息,首先是修改数组里面的元素的值,deArray与deArray1里面放的都是深拷贝对象,在修改数组元素里面的值的属性值的时候,通过打印信息可以发现:深拷贝元素并不会根据修改的元素的属性的值得改变而受到影响(deArray[0]元素的属性copy属性值原本为0,deArray1[0]元素的属性copy属性修改为3,打印后发现只有修改的deArray1[0]的属性值修改了)。而浅拷贝元素却会根据修改的元素的属性的值得更改变而改变(shArray[0]的元素的属性值修改之后,通过打印信息发现,shArray1[0]的元素的属性值也修改了)。

  那么产生了一个问题,数组明明使用的是Swift原生的结构体(值类型),他的拷贝应该是深拷贝,可是之前的结果却显示,shArray1数组内容明明跟着改变了。这是为什么?我们接着看第二部分操作的代码。

  deArray1深拷贝数组在删除了元素之后,元素个数变成1个,原来个数为两个。deArray深拷贝数组并不受影响。再看shArray1浅拷贝数组,原有2个元素,在移除一个元素之后元素个数变成一个,而shArray浅拷贝数组的元素个数并不受影响,依旧是两个。

  那么根据以上打印信息,与总结可以发现这样一个原则:数组在进行赋值操作的时候,都是深拷贝,这一点与数组内元素是引用类型(浅拷贝对象)或者值类型(深拷贝对象)无关,数组在赋值拷贝的时候,首先系统会根据被拷贝数组的大小开辟一样大小的内存空间。然后将原数组中的每一元素依次拷贝到新的数组对象中。

  但是,在修改数组元素的时候又在内容是浅拷贝对象的时候,修改数组内部元素赋值的数组会跟着改变,因此可以得出结论:当数组内部的元素为引用类型的对象时,修改这个元素的属性值,数组的拷贝内部的元素为浅拷贝,即如果修改被拷贝数组的内部元素时,拷贝的数组里面的元素也会跟着变化。反之,内部元素如果是值类型的对象时,数组的拷贝内部元素为深拷贝。

  最后的结论:当仅仅改变了数组的大小(长度),不会影响另外一个数组。

  本篇文章先将内存管理介绍到这里。会有后续文章继续介绍内存管理。      




扫描二维码

关注更多精彩


点击“阅读原文”



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存