為了方面查閱特轉(zhuǎn)一篇文章
Swift的設(shè)計(jì)的初衷就是擺脫ObjC沉重的歷史包袱片任,畢竟ObjC的歷史太過悠久磷斧,相比于很多現(xiàn)代化語言它缺少一些很酷的語法特性遣鼓,而且ObjC的語法和其他語言相比差別很大诀蓉。但是Apple同時(shí)也不能忽視ObjC的地位住诸,畢竟ObjC經(jīng)過二十多年的歷史積累了大量的資源(開發(fā)者、框架箱亿、類庫等)跛锌,因此在Swift推出的初期必須考慮兼容ObjC。但同時(shí)Swift和ObjC是基于兩種不同的方式來實(shí)現(xiàn)的(例如ObjC可以在運(yùn)行時(shí)決定對(duì)象類型届惋,但是Swift為了提高效率要求在編譯時(shí)就必須確定對(duì)象類型)髓帽,所以要無縫兼容需要做大量的工作。而作為開發(fā)人員我們有必要了解兩種語言之間的轉(zhuǎn)化關(guān)系才能對(duì)Swift有更深刻的理解脑豹。
Swift與OC之間的映射
其實(shí)從前面的例子中大家不難發(fā)現(xiàn)Swift和ObjC必然存在著一定的映射關(guān)系郑藏,例如對(duì)于文件的操作使用了字符串的writeToFile方法,在網(wǎng)絡(luò)請(qǐng)求時(shí)使用的NSURLSession瘩欺,雖然調(diào)用方式不同但是其參數(shù)完全和做ObjC開發(fā)時(shí)調(diào)用方式一致必盖。原因就是Swift編譯器自動(dòng)做了映射,下面列舉了部分Swift和ObjC的映射關(guān)系幫助大家理解:
|Swift|Objc|備注|
|::|::|::|
|AnyObject|id|Swift中用到Objc中id類型的參數(shù)被標(biāo)記為對(duì)應(yīng)的可選類型|
|Array俱饿、Dictionary歌粥、Set|NSArray、NSDictionary拍埠、NSSet|Swift中的Int失驶、UInt、Float枣购、Double嬉探、Bool會(huì)自動(dòng)橋接為NSNumber|
|Int|NSInteger、NSUInteger|其他基本類型情況類似棉圈,不再一一列舉|
|NSObjectProtocol|NSObject協(xié)議(注意不是NSObject類)|由于Swift在繼承或者實(shí)現(xiàn)時(shí)沒有類的命名空間的概念涩堤,而ObjC中既有NSObject類又有NSObject協(xié)議,所以在Swift中將NSObject協(xié)議對(duì)應(yīng)成了NSObjectProtocol|
|CGContext|CGContextRef|Core Foundation中其他情況均是如此分瘾,由于Swift本身就是引用類型定躏,在Swift不需要再加上“Ref”|
|ErrorType|NSError||
|“ab:"|@selector(ab:)|Swift可以自動(dòng)將字符串轉(zhuǎn)化成成selector|
|@NSCopying|copy屬性|init(x:X,y:Y) initWithX:(X)x y:(Y)y 構(gòu)造方法映射,Swift會(huì)去掉“With”并且第一個(gè)字母小寫作為其第一個(gè)參數(shù),同時(shí)也不需要調(diào)用alloc方法痊远,但是需要注意ObjC中的便利工廠方法(構(gòu)建對(duì)象的靜態(tài)方法)對(duì)應(yīng)成了Swift的便利構(gòu)造方法|
|func xY(a:A,b:B) |void xY:(A)a b:(B)b| |
|extension(擴(kuò)展) |category(分類) |注意:不能為ObjC中存在的方法進(jìn)行extension|
|Closure(閉包)| block(塊) |注意:Swift中的閉包可以直接修改外部變量垮抗,但是block中要修改外部變量必須聲明為__block|
Swift兼容大部分ObjC(通過類似上面的對(duì)應(yīng)關(guān)系),多數(shù)ObjC的功能在Swift中都能使用碧聪。當(dāng)然冒版,還是有個(gè)別地方Swift并沒有考慮兼容ObjC,例如:Swift中無法使用預(yù)處理指令(例如:宏定義逞姿,事實(shí)上在Swift中推舉使用常量定義)辞嗡;Swift中也無法使用performSelector來執(zhí)行一個(gè)方法,因?yàn)镾wift認(rèn)為這么做是不安全的滞造。
相反续室,如果在ObjC中使用Swift也同樣是可行的(除了個(gè)別Swift新增的高級(jí)功能)。Swift中如果一個(gè)類繼承于NSObject谒养,那么他會(huì)自動(dòng)和ObjC兼容挺狰,這樣ObjC就可以按照上面的對(duì)應(yīng)關(guān)系調(diào)用Swift的方法、屬性等买窟。但是如果Swift中的類沒有繼承于NSObject呢丰泊?此時(shí)就需要使用一個(gè)關(guān)鍵字“@objc”進(jìn)行標(biāo)注,ObjC就可以像使用正常的ObjC編碼一樣調(diào)用Swift了(事實(shí)上繼承于NSObject的類之所以在ObjC中能夠直接調(diào)用也是因?yàn)榫幾g器會(huì)自動(dòng)給類和非private成員添加上@objc始绍,類似的@IBoutlet瞳购、@IBAction、@NSManaged修飾的方法屬性Swift編譯器也會(huì)自動(dòng)添加@objc標(biāo)記)亏推。
Swift中調(diào)用C
由于ObjC是C的超集学赛,使得在ObjC可以無縫訪問C語言。但是Swift的產(chǎn)生就是ObjC without C吞杭,因此在Swift中不可能像在ObjC中混編入C一樣簡單盏浇。但是考慮到C語言的強(qiáng)大以及歷時(shí)那么多年留下了豐富的類庫,有時(shí)候又不得不使用它篇亭,Swift中還是保留了與一定數(shù)量的C語言類型和特性的兼容缠捌。前面介紹過關(guān)于如何在Swift中使用ObjC的知識(shí)锄贷,事實(shí)上在Swift中使用C也是類似的(因?yàn)镺bjC是C的超集译蒂,ObjC既然可以橋接,C自然也可以)谊却,你需要一個(gè)橋接文件柔昼,不同的是ObjC中的很多內(nèi)容在橋接到Swift時(shí)都是類似,很容易上手炎辨。例如ObjC中使用的NSObject捕透,在Swift中仍然對(duì)應(yīng)NSObject,很多時(shí)候開發(fā)人員感覺不到這種轉(zhuǎn)化,只是編程語言發(fā)生了變化乙嘀。但是C導(dǎo)入Swift就需要必須要了解具體的對(duì)應(yīng)關(guān)系:
|C類型|Swift類型 |說明|
|::|::|::|
|基本類型| | |
|char,signed |char CChar|類似的unsigned char對(duì)應(yīng)CUnsignedChar|
|int|CInt|類似的unsigned int對(duì)應(yīng)CUnsignedInt|
|short|CShort|類似的unsigned short對(duì)應(yīng)CUnsignedShort|
|long |CLong|類似的unsigned long對(duì)應(yīng)CUnsignedLong|
|long long |CLongLong|類似的unsigned long long 對(duì)應(yīng) CUnsignedLongLong|
|float |CFloat| |
|double|CDouble||
|構(gòu)造體類型| | 注意:結(jié)構(gòu)體實(shí)現(xiàn)|
|枚舉typedef NS_ENUM(NSInteger,A){AB,AC} |enum A:Int{case B,C}|去掉對(duì)應(yīng)的前綴 ,注意C中的NS_Options會(huì)對(duì)應(yīng)成Swift中實(shí)現(xiàn)OptionSetType的結(jié)構(gòu)體|
|結(jié)構(gòu)體| |對(duì)應(yīng)Swift中的結(jié)構(gòu)體 |
|聯(lián)合 | |Swift中不能完全支持聯(lián)合末购,建議使用枚舉關(guān)聯(lián)值代替|
|指針類型||C語言中的指針類型映射成了Swift中的泛型|
|Type *|UnsafeMutablePointer<Type> |作為返回類型、變量虎谢、參數(shù)類型時(shí)|
|const Type *|UnsafePointer<Type>| 作為返回類型盟榴、變量、參數(shù)類型時(shí)|
|Type *const *|UnsafePointer<Type>| 對(duì)于類類型|
|Type *__strong *|UnsafeMutablePointer<Type>|對(duì)于類類型|
|Type * *| AutoreleasingUnsafePointer<Type>|對(duì)于類類型|
|函數(shù)指針|閉包|||
對(duì)于其他類型的映射關(guān)系都很容易理解婴噩,這里主要說一下指針的內(nèi)容擎场。通過上表可以看到在C中定義的一些指針類型當(dāng)在Swift中使用時(shí)會(huì)有對(duì)應(yīng)的類型,但是如果一個(gè)參數(shù)為某種指針類型几莽,實(shí)際調(diào)用時(shí)應(yīng)該使用何種Swift數(shù)據(jù)類型的數(shù)據(jù)作為參數(shù)調(diào)用呢迅办?例如參數(shù)為UnsafePointer<Type>,是否只能傳入U(xiǎn)nsafePointer<Type>呢章蚣,其實(shí)也可以傳入nil站欺,并且最終調(diào)用時(shí)將會(huì)轉(zhuǎn)化為null指針來調(diào)用。下表列出了這種參數(shù)調(diào)用對(duì)應(yīng)關(guān)系:
|可用類型|最終轉(zhuǎn)化類型|
|::|::|
|UnsafePointer<Type> | 注意:如果Type為Void則可以代表任何類型|
| nil | null|
|UnsafePointer<Type>究驴、UnsafeMutablePointer<Type>镊绪、AutoreleasingUnsafeMutablePointer<Type>| UnsafePointer<Type>|
| String |如果Type為Int、或者Int8將最終轉(zhuǎn)化為UTF8字符串|
| &typeValue| 元素地址|
| Type類型的數(shù)組([typeValue1,typeValue2])| 數(shù)組首地址|
| UnsafeMutablePointer<Type> | 注意:如果Type為Void則可以代表任何類型|
| nil| null |
|UnsafeMutablePointer<Type> | UnsafeMutablePointer<Type> |
| &typeValue| 元素地址|
|Type類型的數(shù)組的地址(&[typeValue1,typeValue2]) |數(shù)組地址|
| AutoreleasingUnsafeMutablePointer<Type>||
| nil| null |
| AutoreleasingUnsafeMutablePointer<Type> | AutoreleasingUnsafeMutablePointer<Type>|
| &typeValue | 元素地址|
下面不妨看一下如何在Swift中使用C語言洒忧,假設(shè)現(xiàn)在有一個(gè)用于字符串拼接的C庫函數(shù)“stringAppend(char*,const char *)”蝴韭,將其對(duì)應(yīng)的文件導(dǎo)入到一個(gè)Swift項(xiàng)目中按照提示添加橋接頭文件并在橋接頭文件中引入對(duì)應(yīng)的C文件。
string.h
#ifndef __UseCInSwift__Common__
#define __UseCInSwift__Common__void stringAppend(char *source, char *toAppend)
#include
void stringAppend(char *source,const char *toAppend);
#endif
string.c
#include "string.h"
void stringAppend(char *source,const char *toAppend) {
unsigned long sourceLen = strlen(source);
char *pSource = source + sourceLen;
const char *pAppend = toAppend;
while (*pAppend != '\0') {
*pSource++ = *pAppend++;
}
}
UseCinSwift-Briding-Header.h
#import "string.h"
然后在Swift中調(diào)用上面的C函數(shù)
import Foundation
var sourceStr:String = "Hello"
var appendStr:String = ",World!"
var sourceCStr = (sourceStr as NSString).UTF8String
var sourceMutablePointer:UnsafeMutablePointer = UnsafeMutablePointer(sourceCStr)
stringAppend(sourceMutablePointer,appendStr)
println(String.fromCString(sourceMutablePointer)!) //結(jié)果:Hello,World!
當(dāng)然熙侍,上面這種方式適合所有在Swift中引入C語言的情況榄鉴,但是為了方便調(diào)用,在Swift中默認(rèn)已經(jīng)module了常用的C語言類庫Darwin蛉抓,這個(gè)類庫就作為了標(biāo)準(zhǔn)的Swift類庫不需要再進(jìn)行橋接庆尘,可以直接導(dǎo)入模塊(例如import Darwin,但是事實(shí)上Foundation模塊已經(jīng)默認(rèn)導(dǎo)入了Darwin巷送,而UIKit又導(dǎo)入了Foundation模塊驶忌,因此通常不需要手動(dòng)導(dǎo)入Darwin)。那么對(duì)于沒有模塊化的C語言類庫(包括第三方類庫和自己定義的C語言文件等)能不能不使用橋接文件呢笑跛?答案就是使用隱藏符號(hào)“@asmname”付魔,通過@asmname可以將C語言的函數(shù)不經(jīng)過橋接文件直接映射為Swift函數(shù)。例如可以移除上面的橋接頭文件飞蹂,修改main.swift函數(shù)几苍,通過@asmname加stringAppend映射成為Swift函數(shù)(注意重新映射的Swift函數(shù)名稱不一定和C語言函數(shù)相同):
import Foundation
//通過asmname將C函數(shù)stringAppend()映射到Swift函數(shù),事實(shí)上這里的Swift函數(shù)名可以任意命名
@asmname("stringAppend") func stringAppend(var sourceStr:UnsafeMutablePointer,var apendStr:UnsafePointer ) -> Void
var sourceStr:String = "Hello"
var appendStr:String = ",World!"
var sourceCStr = (sourceStr as NSString).UTF8String
var sourceMutablePointer:UnsafeMutablePointer = UnsafeMutablePointer(sourceCStr)
stringAppend(sourceMutablePointer,appendStr)
println(String.fromCString(sourceMutablePointer)!) //結(jié)果:Hello,World!
反射
熟悉C#、Java的朋友不難理解反射的概念陈哑,所謂反射就是可以動(dòng)態(tài)獲取類型妻坝、成員信息伸眶,在運(yùn)行時(shí)可以調(diào)用方法、屬性等行為的特性刽宪。 在使用ObjC開發(fā)時(shí)很少強(qiáng)調(diào)其反射概念厘贼,因?yàn)镺bjC的Runtime要比其他語言中的反射強(qiáng)大的多。在ObjC中可以很簡單的實(shí)現(xiàn)字符串和類型的轉(zhuǎn)換(NSClassFromString())圣拄,實(shí)現(xiàn)動(dòng)態(tài)方法調(diào)用(performSelector: withObject:),動(dòng)態(tài)賦值(KVC)等等涂臣,這些功能大家已經(jīng)習(xí)以為常,但是在其他語言中要實(shí)現(xiàn)這些功能卻要跨過較高的門檻售担,而且有些根本就是無法實(shí)現(xiàn)的赁遗。不過在Swift中并不提倡使用Runtime,而是像其他語言一樣使用反射(Reflect)族铆,即使目前Swift中的反射還沒有其他語言中的反射功能強(qiáng)大(Swift還在發(fā)展當(dāng)中岩四,相信后續(xù)版本會(huì)加入更加強(qiáng)大的反射功能)。
在Swift中反射信息通過MirrorType協(xié)議來描述哥攘,而Swift中所有的類型都能通過reflect函數(shù)取得MirrorType信息剖煌。先看一下MirrorType協(xié)議的定義(為了方便大家理解,添加了相關(guān)注釋說明):
protocol MirrorType {
/// 被反射的成員逝淹,類似于一個(gè)實(shí)例做了as Any操作
var value: Any { get }
/// 被反射成員的類型
var valueType: Any.Type { get }
/// 被反射成員的唯一標(biāo)識(shí)
var objectIdentifier: ObjectIdentifier? { get }
/// 被反射成員的子成員數(shù)(例如結(jié)構(gòu)體的成員個(gè)數(shù)耕姊,數(shù)組的元素個(gè)數(shù)等)
var count: Int { get }
// 取得被反射成員的字成員,返回值對(duì)應(yīng)字成員的名稱和值信息
subscript (i: Int) -> (String, MirrorType) { get }
/// 對(duì)于反射成員的描述
var summary: String { get }
/// 顯示在Playground中的“值”信息
var quickLookObject: QuickLookObject? { get }
/// 被反射成員的類型的種類(例如:基本類型栅葡、結(jié)構(gòu)體茉兰、枚舉、類等)
var disposition: MirrorDisposition { get }
}
獲取到一個(gè)變量(或常量)的MirrorType之后就可以訪問其類型欣簇、值规脸、類型種類等元數(shù)據(jù)信息。在下面的示例中將編寫一個(gè)函數(shù)簡單實(shí)現(xiàn)一個(gè)類似于ObjC中“valueForKey:”的函數(shù)熊咽。
import UIKit
struct Person {
var name:String
var age:Int = 0
func showMessage(){
print("name=\(name),age=\(age)")
}
}
//定義一個(gè)方法獲取實(shí)例信息
func valueForKey(key:String,obj:Any) -> Any?{
//獲取元數(shù)據(jù)信息
var objInfo:MirrorType = reflect(obj)
//遍歷子成員
for index in 0..<objInfo.count {
//如果子成員名稱等于key則獲取對(duì)應(yīng)值
let (name,mirror) = objInfo[index]
if name == key {
return mirror.value
}
}
return nil;
}
var p = Person(name: "Kenshin", age: 29)
//先查看一下對(duì)象描述信息莫鸭,然后對(duì)照結(jié)果是否正確
dump(p)
/*結(jié)果:
__lldb_expr_103.Person
- name: Kenshin
- age: 29
*/
var name = valueForKey("name", p)
print("p.name=\(name)") //結(jié)果:p.name=Optional("Kenshin")
可以看到,通過反射可以獲取到變量(或常量)的信息横殴,并且能夠讀取其成員的值被因,但是Swift目前原生并不支持給某個(gè)成員動(dòng)態(tài)設(shè)置值(MirrorType的value屬性是只讀的)。如果想要進(jìn)行動(dòng)態(tài)設(shè)置衫仑,可以利用前面介紹的Swift和ObjC兼容的知識(shí)來實(shí)現(xiàn)梨与,Swift目前已經(jīng)導(dǎo)入了Foundation,只要這個(gè)類是繼承于NSObject就會(huì)有對(duì)應(yīng)的setValue:forKey:方法來使用KVC惑畴。當(dāng)然蛋欣,這僅限于類航徙,對(duì)應(yīng)結(jié)構(gòu)體無能為力如贷。
KVO
和KVC一樣陷虎,在Swift中使用KVO也僅限于NSObject及其子類,因?yàn)镵VO本身就是基于KVC進(jìn)行動(dòng)態(tài)派發(fā)的杠袱,這些都屬于運(yùn)行時(shí)的范疇尚猿。Swift要實(shí)現(xiàn)這些動(dòng)態(tài)特性需要在類型或者成員前面加上@objc(繼承于NSObject的子類及非私有成員會(huì)自動(dòng)添加),但并不是說加了@objc就可以動(dòng)態(tài)派發(fā)楣富,因?yàn)镾wift為了性能考慮會(huì)優(yōu)化為靜態(tài)調(diào)用凿掂。如果確實(shí)需要使用這些特性Swift提供了dynamic關(guān)鍵字來修飾,例如這里要想使用KVO除了繼承于NSObject之外就必須給監(jiān)控的屬性加上dynamic關(guān)鍵字修飾纹蝴。下面的演示中說明了這一點(diǎn):
import Foundation
class Acount:NSObject {
dynamic var balance:Double = 0.0
}
class Person:NSObject {
var name:String
var account:Acount?{
didSet{
if account != nil {
account!.addObserver(self, forKeyPath: "balance", options: .Old, context: nil);
}
}
}
init(name:String){
self.name = name
super.init()
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer) {
if keyPath == "balance" {
var oldValue = change[NSKeyValueChangeOldKey] as! Double
var newValue = (account?.balance)!
print("oldValue=\(oldValue),newValue=\(newValue)")
}
}
}
var p = Person(name: "Kenshin Cui")
var account = Acount()
account.balance = 10000000.0
p.account = account
p.account!.balance = 999999999.9 //結(jié)果:oldValue=10000000.0,newValue=999999999.9
注意:對(duì)于系統(tǒng)類(或一些第三方框架)由于無法修改其源代碼如果要進(jìn)行KVO監(jiān)聽庄萎,可以先繼承此類然后進(jìn)行使用dynamic重寫;此外,并非只有KVO需要加上dynamic關(guān)鍵字塘安,對(duì)于很多動(dòng)態(tài)特性都是如此糠涛,例如要在Swift中實(shí)現(xiàn)Swizzle方法替換,方法前仍然要加上dynamic兼犯,因?yàn)榉椒ǖ奶鎿Q也需要?jiǎng)討B(tài)派發(fā)忍捡。
內(nèi)存管理
循環(huán)引用
Swift使用ARC來自動(dòng)管理內(nèi)存,大多數(shù)情況下開發(fā)人員不需要手動(dòng)管理內(nèi)存切黔,但在使用ObjC開發(fā)時(shí)砸脊,大家都會(huì)遇到循環(huán)引用的問題,在Swift中也不可避免纬霞。 舉例來說凌埂,人員有一個(gè)身份證(Person有idCard屬性),而身份證就有一個(gè)擁有者(IDCard有owner屬性)诗芜,那么對(duì)于一個(gè)Person對(duì)象一旦建立了這種關(guān)系之后就會(huì)和IDCard對(duì)象相互引用而無法被正確的釋放侨舆。
例如下面的代碼在執(zhí)行完test之后p和idCard兩個(gè)對(duì)象均不會(huì)被釋放:
import Foundation
class Person {
var name:String
var idCard:IDCard
init(name:String,idCard:IDCard){
self.name = name
self.idCard = idCard
idCard.owner = self
}
deinit{
println("Person deinit...")
}
}
class IDCard {
var no:String
var owner:Person?
init(no:String){
self.no = no
}
deinit{
println("IDCard deinit...")
}
}
func test(){
var idCard = IDCard(no:"100188888888888888")
var p = Person(name: "Kenshin Cui",idCard:idCard)
}
//注意test執(zhí)行完之后p和idCard均不會(huì)被釋放(無法執(zhí)行deinit方法)
test()
println("wait...")
為了避免這個(gè)問題Swift采用了和ObjC中同樣的概念:弱引用,通常將被動(dòng)的一方的引用設(shè)置為弱引用來解決循環(huán)引用問題绢陌。例如這里可以將IDCard中的owner設(shè)置為弱引用挨下。因?yàn)镮DCard對(duì)于Person的引用變成了弱引用,而Person持有IDCard的強(qiáng)引用脐湾,這樣一來Person作為主動(dòng)方臭笆,只要它被釋放后IDCard也會(huì)跟著釋放。如要聲明弱引用可以使用weak和unowned關(guān)鍵字秤掌,前者用于可選類型后者用于非可選類型愁铺,相當(dāng)于ObjC中的__weak和__unsafe_unretained(因?yàn)閣eak聲明的對(duì)象釋放后會(huì)設(shè)置為nil,因此它用來修飾可選類型)闻鉴。
import Foundation
class Person {
var name:String
var idCard:IDCard
init(name:String,idCard:IDCard){
self.name = name
self.idCard = idCard
idCard.owner = self
}
deinit{
println("Person deinit...")
}
}
class IDCard {
var no:String
//聲明為弱引用
weak var owner:Person?
init(no:String){
self.no = no
}
deinit{
println("IDCard deinit...")
}
}
func test(){
var idCard = IDCard(no:"100188888888888888")
var p = Person(name: "Kenshin Cui",idCard:idCard)
}
//注意test執(zhí)行完之后p會(huì)被釋放茵乱,其后idCard跟著被釋放
test()
println("wait...")
當(dāng)然類似于上面的引用關(guān)系實(shí)際遇到的并不多,更多的還是存在于閉包之中(ObjC中多出現(xiàn)于Block中)孟岛,因?yàn)殚]包會(huì)持有其內(nèi)部引用的元素瓶竭。下面簡單修改一下上面的例子督勺,給Person添加一個(gè)閉包屬性,并且在其中訪問self斤贰,這樣閉包自身就和Person類之間形成循環(huán)引用智哀。
import Foundation
class Person {
let name:String
//使用閉包捕獲列表解決循環(huán)引用
lazy var description:()->NSString = {
[unowned self] in
return "name = \(self.name)"
}
init(name:String){
self.name = name
}
deinit{
println("Person deinit...")
}
}
func test(){
var p = Person(name: "Kenshin Cui")
println(p.description())
}
test()
println("wait...")
/**打印結(jié)果
name = Kenshin Cui
Person deinit...
wait...
*/
指針與內(nèi)存
除了循環(huán)引用問題,Swift之所以將指針類型標(biāo)識(shí)為“unsafe”是因?yàn)橹羔槢]辦法像其他類型一樣進(jìn)行自動(dòng)內(nèi)存管理荧恍,因此有必要了解一下指針和內(nèi)存的關(guān)系瓷叫。在Swift中初始化一個(gè)指針必須通過alloc和initialize兩步,而回收一個(gè)指針需要調(diào)用destroy和dealloc(通常dealloc之后還會(huì)將指針設(shè)置為nil)送巡。
import Foundation
class Person {
var name:String
init(name:String){
self.name = name
}
deinit{
println("Person\(name) deinit...")
}
}
func test(){
var p = Person(name: "Kenshin Cui")
//雖然可以使用&p作為參數(shù)進(jìn)行inout參數(shù)傳遞摹菠,但是無法直接獲取其地址,下面的做法是錯(cuò)誤的
//var address = &p
/*創(chuàng)建一個(gè)指向Person的指針pointer*/
//申請(qǐng)內(nèi)存(alloc參數(shù)代表申請(qǐng)n個(gè)Person類型的內(nèi)存)
var pointer:UnsafeMutablePointer = UnsafeMutablePointer.alloc(1)
//初始化
pointer.initialize(p)
//獲取指針指向的對(duì)象
var p2 = pointer.memory
println(p===p2) //結(jié)果:true骗爆,因?yàn)閜和p2指向同一個(gè)對(duì)象
//修改對(duì)象的值
p2.name = "Kaoru"
println(p.name) //結(jié)果:Kaoru
//銷毀指針
pointer.destroy()
//釋放內(nèi)存
pointer.dealloc(1)
//指向空地址
pointer = nil
}
test()
println("waiting...")
/**打印結(jié)果
Kaoru
PersonKaoru deinit...
waiting...
*/
運(yùn)行程序可以看到p對(duì)象在函數(shù)執(zhí)行結(jié)束之后被銷毀辨嗽,但是如果僅僅將pointer設(shè)置為nil是無法銷毀Person對(duì)象的,這很類似于之前的MRC內(nèi)存管理淮腾,在Swift中使用指針需要注意:誰創(chuàng)建(alloc,malloc,calloc)誰釋放糟需。 當(dāng)然上面演示中顯然對(duì)于指針的操作略顯麻煩,如果需要對(duì)一個(gè)變量進(jìn)行指針操作可以借助于Swift中提供的一個(gè)方法withUnsafePointer谷朝。例如想要利用指針修改Person的name就可以采用下面的方式:
var p = Person(name: "Kenshin Cui")
var p2 = withUnsafeMutablePointer(&p, {
(pointer:UnsafeMutablePointer) -> Person in
pointer.memory.name = "Kaoru"
return pointer.memory
})
println(p.name) //結(jié)果:Kaoru
在前面的C語言系列文章中有一部分內(nèi)容用于介紹如何利用指針遍歷一個(gè)數(shù)組洲押,當(dāng)然在Swift中仍然可以采用這種方式,但是在Swift中如果想要使用指針操作數(shù)組中每個(gè)元素的話通常借助于另一個(gè)類型UnsafeMutableBufferPointer圆凰。這個(gè)類表示一段連續(xù)內(nèi)存杈帐,通常用于表示數(shù)組或字典的指針類型。
import Foundation
var array:[String] = ["Kenshin","Kaorsu","Tom"]
//UnsafeBufferPointer和UnsafeMutableBufferPointer用于表示一段連續(xù)內(nèi)存的指針专钉,例如:數(shù)組或字典
//下面創(chuàng)建一個(gè)指向數(shù)組的指針
var pointer = UnsafeMutableBufferPointer(start: &array, count: 3)
//baseAddress屬性表示內(nèi)存首地址
var baseAddress = pointer.baseAddress as UnsafeMutablePointer
println(baseAddress.memory) //結(jié)果:Kenshin
//利用指針遍歷數(shù)組
for index in 1...pointer.count {
println(baseAddress.memory)
//向后移動(dòng)指針,向前移動(dòng)使用baseAddress.predecessor()
baseAddress = baseAddress.successor()
}
/**打印結(jié)果
Kenshin
Kaorsu
Tom
*/
Core Foundation
Core Foundation作為iOS開發(fā)中最重要的框架之一挑童,在iOS開發(fā)中有著重要的地位,但是它是一組C語言接口跃须,在使用時(shí)需要開發(fā)人員自己管理內(nèi)存站叼。在Swift中使用Core Foundation框架(包括其他Core開頭的框架)需要區(qū)分這個(gè)API返回的對(duì)象是否進(jìn)行了標(biāo)注:
1.如果已經(jīng)標(biāo)注則在使用時(shí)完全不用考慮內(nèi)存管理(它可以自動(dòng)管理內(nèi)存)。
2.如果沒有標(biāo)注則編譯器不會(huì)進(jìn)行內(nèi)存管理托管菇民,此時(shí)需要將這個(gè)非托管對(duì)象轉(zhuǎn)化為托管對(duì)象(當(dāng)然你也可以使用retain()尽楔、release()或者autorelease()手動(dòng)管理內(nèi)存,但是不推薦這么做)第练。當(dāng)然阔馋,蘋果開發(fā)工具組會(huì)盡可能的標(biāo)注這些API以實(shí)現(xiàn)C代碼和Swift的自動(dòng)橋接,但是在此之前未標(biāo)注的API會(huì)返回Unmanaged<Type>結(jié)構(gòu)娇掏,可以調(diào)用takeUnretainedValue()和takeRetainedValue()方法將其轉(zhuǎn)化為可以自動(dòng)進(jìn)行內(nèi)存管理的托管對(duì)象(具體是調(diào)用前者還是后者呕寝,需要根據(jù)是否需要開發(fā)者自己進(jìn)行內(nèi)存管理而定,其本質(zhì)是使用takeRetainedValue()方法婴梧,在對(duì)象使用完之后會(huì)調(diào)用一次release()方法下梢。按照Core Foundation的命名標(biāo)準(zhǔn)客蹋,通常如果函數(shù)名中含“Create”晶衷、“Copy”奋岁、“Retain”關(guān)鍵字需要調(diào)用takeRetainedValue()方法來轉(zhuǎn)化成托管對(duì)象)他去。
當(dāng)然,上述兩種方式均是針對(duì)系統(tǒng)框架而言竟坛,如果是開發(fā)者編寫的類或者第三方類庫,應(yīng)該盡可能按照Cocoa規(guī)范命名并且在合適的地方使用CF_RETURNS_RETAINED和CF_RETURNS_NOT_RETAINED來進(jìn)行標(biāo)注以便可以進(jìn)行自動(dòng)內(nèi)存管理