Tag Archives: Swift

Swift中文教程(13)继承

转载自letsswift.com

一个类可以从另外一个类中继承方法,属性或者其它的一些特性。当一个类继承于另外一个类时,这个继承的类叫子类,被继承的类叫父类。继承是Swift中类区别于其它类型的一个基本特征。

Swift中的类可以调用父类的方法,使用父类的属性和下标,还可以根据需要使用重写方法或者属性来重新定义和修改他们的一些特性。Swift可以帮助你检查重写的方法和父类的方法定义是相符的。

类还可以为它继承的属性添加观察者,这样可以能够让它在一个属性变化的时候得到通知。属性观察者可以被添加给任何属性,不管它之前是存储属性还是计算属性。

1、定义一个基类

任何一个不继承于其它类的类被称作基类

注意:Swift的类不是从一个全局基类继承而来。在你编写代码的时,只要是在类的定义中没有继承自父类的类都是基类。

下面的例子定义了一个叫Vehicle的基类。基类包含两个所有交通工具通用的属性numberOfWheels和maxPassengers。这两个属性被一个叫description的方法使用,通过返回一个String描述来作为这个交通工具的特征:

1234567891011
classVehicle{
    var numberOfWheels:Int
    var maxPassengers:Int
    func description()->String{
        return"\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
    }
    init(){
        numberOfWheels =0
        maxPassengers =1
    }}

这个交通工具类Vehicle还定义了一个构造函数来设置它的属性。构造函数更多的解释在Initialization一章,但是为了说明子类如何修改继承的属性,这里需要简要解释一下什么叫构造函数。

通过构造函数可以创建一个类型的实例。尽管构造函数不是方法,但是它们在编码的时候使用了非常相似的语法。构造函数通过确保所有实例的属性都是有效的来创建一个新的实例。

构造函数最简单的形式是使用init关键词的一个类似方法的函数,并且没有任何参数:

123
init(){
    // perform some initialization here}

使用构造函数语法TypeName和空的两个小括号来完成一个Vehicle实例的创建:

1
let someVehicle =Vehicle()

Vehicle的构造函数为属性设置了一些初始值(numberOfWheels = 0 然后 maxPassengers = 1)。

Vehicle类定义的是一个通用的交通工具特性,它本身没有太多意义,所以就需要冲定义它的一些属性或者方法来让它具有实际的意义。

2、产生子类

产生子类就是根据一个已有的类产生新类的过程。子类继承了父类的一些可以修改的特性。还可以为子类添加一些新的特性。

为了表明一个类是继承自一个父类,需要将父类的名称写在子类的后面,并且用冒号分隔:

123
classSomeClass:SomeSuperclass{
    // class definition goes here}

下面的例子定义了一种特定叫Bicycle的交通工具。这个新类是基于已有的类Vehicle产生的。书写方式是在类名Bicycle后加冒号加父类Vehicle名。

可以理解为:

定义一个新的类叫Bicycle,它继承了Vehicle的特性:

123456
classBicycle:Vehicle{
    init(){
        super.init()
        numberOfWheels =2
    }}

Bicycle是Vehicle的子类,Vehicle是Bicycle的父类。Bicycle类继承了Vehicle所有的特征,比如maxPassengers和numberOfWheels属性。你还可以为Bicycle类添加心的属性。

Bicycle类也定义了构造函数,在这个构造函数中调用了父类的构造函数super.init(),这样可以确保在Bicycle修改他们之前,父类已经初始化了。

注意:跟Objective-C不同的是,Swift中的构造函数没有默认继承。更多信息可以参考Initializer Inheritance and Overriding这一章节。

maxPassengers属性在继承自父类的时候已经被初始化了,对于Bicycle来说是正确的,因此不需要再做更改。然后numberOfWheels是不对的,所以被替换成了2.

不仅属性是继承于Vehicle的,Bicycle还继承了父类的方法。如果你创建一个实例,然后调用了已经继承的description方法,可以得到该交通工具的描述并且看到它的属性已经被修改:

123
let bicycle =Bicycle()
println("Bicycle: \(bicycle.description())")// Bicycle: 2 wheels; up to 1 passengers

子类本身也可以作为父类被再次继承:

123456
classTandem:Bicycle{
    init(){
        super.init()
        maxPassengers =2
    }}

上面的例子创建了Bicycle的子类,叫做tandem,也就可以两个人一起骑的自行车。所以Tandem没有修改numberOfWheels属性,只是更新了maxPassengers属性。

注意:子类只能够在构造的时候修改变量的属性,不能修改常量的属性。

创建一个Tandem的实例,然后调用description方法检查属性是否被正确修改:

123
let tandem =Tandem()
println("Tandem: \(tandem.description())")// Tandem: 2 wheels; up to 2 passengers

注意到description方法也被Tandem继承了。

3、重写方法

子类可以提供由父类继承来的实例方法,类方法,实例属性或者下标的个性化实现。这个特性被称为重写。

重写一个由继承而来的方法需要在方法定义前标注override关键词。通过这样的操作可以确保你所要修改的这个方法确实是继承而来的,而不会出现重写错误。错误的重写会造成一些不可预知的错误,所以如果如果不标记override关键词的话,就会被在代码编译时报错。

override关键词还能够让Swift编译器检查该类的父类是否有相符的方法,以确保你的重写是可用的,正确的。

访问父类方法,属性和下标

当在重写子类继承自父类的方法,属性或者下标的时候,需要用到一部分父类已有的实现。比如你可以重定义已知的一个实现或者在继承的变量中存储一个修改的值。

适当的时候,可以通过使用super前缀来访问父类的方法,属性或者下标:

叫someMethod的重写方法可以在实现的时候通过super.someMethod()调用父类的someMethod方法。

叫someProperty的重写属性可以在重写实现getter或者setter的时候通过super.someProperty调用父类的someProperty。

叫someIndex的重写下标可以在实现下标的时候通过super[someIndex]来访问父类的下标。

复写方法

你可以在你的子类中实现定制的继承于父类的实例方法或者类方法。

下面的例子演示的就是一个叫Car的Vehicle子类,重写了继承自Vehicle的description方法。

123456789101112
classCar:Vehicle{
    var speed:Double=0.0
    init(){
        super.init()
        maxPassengers =5
        numberOfWheels =4
    }
    override func description()->String{
        returnsuper.description()+"; "
            +"traveling at \(speed) mph"
    }}

Car中定义了一个新的Double类型的存储属性speed。这个属性默认值是0.0,意思是每小时0英里。Car还有一个自定义的构造函数,设置了最大乘客数为5,轮子数量是4.

Car重写了继承的description方法,并在方法名description前标注了override关键词。

在description中并没有给出了一个全新的描述实现,还是通过super.description使用了Vehicle提供的部分描述语句,然后加上了自己定义的一些属性,如当前速度。

如果你创建一个Car的实例,然后调用description方法,会发现描述语句变成了这样:

123
let car =Car()
println("Car: \(car.description())")// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph

复写属性

你还可以提供继承自父类的实例属性或者类属性的个性化getter和setter方法,或者是添加属性观察者来实现重写的属性可以观察到继承属性的变动。

重写属性的Getters和Setters

不管在源类中继承的这个属性是存储属性还是计算属性,你都可以提供一个定制的getter或者setter方法来重写这个继承属性。子类一般不会知道这个继承的属性本来是存储属性还是计算属性,但是它知道这个属性有特定的名字和类型。在重写的时候需要指明属性的类型和名字,好让编译器可以检查你的重写是否与父类的属性相符。

你可以将一个只读的属性通过提那家getter和setter继承为可读写的,但是反之不可。

注意:如果你为一个重写属性提供了setter方法,那么也需要提供getter方法。如果你不想在getter中修改继承的属性的值,可以在getter中使用super.someProperty即可,在下面SpeedLimitedCar例子中也是这样。

下面的例子定义了一个新类SpeedLimitedCar,是Car的一个子类。这个类表示一个显示在40码一下的车辆。通过重写继承的speed属性来实现:

12345678910
classSpeedLimitedCar:Car{
    overridevar speed:Double  {
    get{
        returnsuper.speed
    }
    set{
        super.speed =min(newValue,40.0)
    }
    }}

每当你要设置speed属性的时候,setter都会检查新值是否比40大,二者中较小的值会被设置给SpeedLimitedCar。

如果你尝试为speed设置超过40的值,description的输出依然还是40:

1234
let limitedCar =SpeedLimitedCar()
limitedCar.speed =60.0
println("SpeedLimitedCar: \(limitedCar.description())")// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph

重写属性观察者

你可以使用属性重写为继承的属性添加观察者。这种做法可以让你无论这个属性之前是如何实现的,在继承的这个属性变化的时候都能得到提醒。更多相关的信息可以参考Property Observers这章。

注意:不能为继承的常量存储属性或者是只读计算属性添加观察者。这些属性值是不能被修改的,因此不适合在重写实现时添加willSet或者didSet方法。

注意:不能同时定义重写setter和重写属性观察者,如果想要观察属性值的变化,并且又为该属性给出了定制的setter,那只需要在setter中直接获得属性值的变化就行了。

下面的代码演示的是一个新类AutomaticCar,也是Car的一个子类。这个类表明一个拥有自动变速箱的汽车,可以根据现在的速度自动选择档位,并在description中输出当前档位:

1234567891011
classAutomaticCar:Car{
    var gear =1
    overridevar speed:Double{
    didSet {
        gear =Int(speed /10.0)+1
    }
    }
    override func description()->String{
        returnsuper.description()+" in gear \(gear)"
    }}

这样就可以实现,每次你设置speed的值的时候,didSet方法都会被调用,来看档位是否需要变化。gear是由speed除以10加1计算得来,所以当速度为35的时候,gear档位为4:

1234
let automatic =AutomaticCar()
automatic.speed =35.0
println("AutomaticCar: \(automatic.description())")// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4

4、禁止重写

你可以通过标记final关键词来禁止重写一个类的方法,属性或者下标。在定义的关键词前面标注@final属性即可。

在子类中任何尝试重写父类的final方法,属性或者下标的行为都会在编译时报错。同样在扩展中为类添加的方法,属性或者下标也可以被标记为final。

还可以在类关键词class前使用@final标记一整个类为final(@final class)。任何子类尝试继承这个父类时都会在编译时报错。

感谢翻译小组成员:李起攀(微博)、若晨(微博)、YAO、粽子、山有木兮木有枝、渺-Bessie、墨离、矮人王、CXH、Tiger大顾(微博)
个人转载请注明出处和原始链接http://letsswift.com/2014/06/inheritance,商业转载请联系我们~ 感谢您对我们工作的支持~

Using Swift with Cocoa and OC与Cocoa和OC一起使用Swift

Swift系列文章由CocoaChina翻译小组翻译自苹果的官方文档,本篇译者:@Creolophus,敬请勘误!

互用性是让Swift和Objective-C相接合的一种特性,使你能够在一种语言编写的文件中访问和使用另一种语言编写的代码。当你准备开始把Swift融入到你的开发流程中时,你应该懂得如何利用互用性来重新定义并提高你写Cocoa应用的方式。互用性很重要的一点就是当你在写Swift代码时使用Objective-C的API接口。当你导入一个Objective-C框架后,你可以使用原生的Swift语法实例化它的Class并且与之交互。

初始化(Initialization)

为了使用Swift实例化Objective-C的Class,你应该使用Swift语法调用它的一个初始化器。当Objective-C的init方法变化到Swift,它们用Swift初始化语法呈现。”init”前缀被截断当作一个关键字,用来表明该方法是初始化方法。那些以“initWith”开头的init方法,“With”也会被去除。从“init”或者“initWith”中分离出来的这部分方法名首字母变成小写,并且被当做是第一个参数的参数名。其余的每一部分方法名依次变为参数名。这些方法名都在圆括号中被调用。

举个例子,你在使用Objective-C时会这样做:

UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];

在Swift中,你应该这样做:

let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)

你不需要调用alloc,Swift能替你处理。注意,当使用Swift风格的初始化函数的时候,”init”不会出现。

你可以在初始化时显式地声明对象的类型,或者也可以忽略它,Swift的类型接口能够正确判断对象的类型。

//Swift
let myTextField = UITextField(frame: CGRect(0.0, 0.0, 200.0, 40.0))

这里的UITableView和UITextField对象和你在Objective-C中使用的具有相同的功能。你可以用样的方式使用他们,包括访问属性或者调用各自的类中定义的方法。

为了统一和简易,Objective-C的工厂方法也在Swift中映射为方便的初始化方法。这种映射能够让他们使用同样简洁明了的初始化方法。例如,在Objective-C中你可能会像下面这样调用一个工厂方法:

//Objective-C
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];

在Swift中,你应该这样做:

//Swift
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)

访问属性(Accessing Properties)

在Swift中访问和设置Objective-C对象的属性时,使用点语法

///Swift
myTextField.textColor = UIColor.darkGrayColor()
myTextField.text = “Hello world”
if myTextField.editing {
myTextField.editing = false
}

当getting或setting属性时,直接使用属性名称,不需要附加圆括号。注意,darkGrayColor后面附加了一对圆括号,这是因为darkGrayColor是UIColor的一个类方法,不是一个属性。 在Objective-C中,一个有返回值的无参数方法可以被作为一个隐式的访问函数(implicit getter),并且可以与访问器使用同样的方法调用。但在Swift中不再能够这样做了,只有使用Objective-C中@property语法编写的属性才能被作为属性引入。详细请参看Working with Methods。

使用方法(Working with Methods)

在Swift中调用Objective-C方法时,使用点语法。 当Objective-C方法转换到Swift时,Objective-C的selector的第一部分将会成为基本方法名并出现在圆括号的前面,而第一个参数将直接在括号中出现,并且没有参数名,而剩下的参数名与参数则一一对应地填入圆括号中。

举个例子,你在使用Objective-C时会这样做:

//Objective-C
[myTableView insertSubview:mySubview atIndex:2];

在Swift中,你应该这样做:

//Swift
myTableView.insertSubview(mySubview, atIndex: 2)

如果你调用一个无参方法,仍必须在方法名后面加上一对圆括号

//Swift
myTableView.layoutIfNeeded()

id 兼容性(id Compatibility)

Swift包含一个叫做AnyObject的协议类型,用以表示任意类型的对象,就像Objective-C中的id一样。AnyObject协议允许你编写类型安全的Swift代码,同时维持无类型对象的灵活性。因为AnyObject协议也保证了这种安全,Swift将id对象导入为AnyObject。

举个例子,跟id一样,你可以为AnyObject类型的对象分配任何其他类型的对象,你也同样可以为它重新分配其他类型的对象。

//Swift
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()

你也可以在调用Objective-C方法或者访问属性时不将它转换为具体类的类型。这包括了Objcive-C中标记为@objc的方法。

//Swift
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow

然而,由于直到运行时才知道AnyObject的对象类型,所以有可能在不经意间写出不安全代码。另外,与Objective-C不同的是,如果你调用的方法或者访问的属性没有经AnyObject对象声明,运行时将会报错。比如下面的代码在运行时将会报出一个未被识别的selector error:

//Swift
myObject.characterAtIndex(5)
// crash, myObject does’t respond to that method

但是,你可以通过Swift的optinals特性来排除这个Objective-C中常见的错误。当你用AnyObject对象调用一个Objective-C方法时,这次调用将会变成一次隐式展开optional(implicitly unwrapped optional)的行为。你可以通过optional特性来决定AnyObject类型的对象是否调用该方法,同样的,你可以把这种特性应用在属性上。

举个例子,在下面的代码中,第一和第二行代码将不会被执行,因为length属性和characterAtIndex:方法不存在于NSDate对象中。myLength常量会被推测成可选的Int类型并且被赋值为nil。同样你可以使用if-let声明来有条件的展开这个方法的返回结果,从而判断对象是否能执行这个方法。就像第三行做的一样。

//Swift
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println(“Found \(fifthCharacter) at index 5”)
}

对于Swift中的强制类型转换,从AnyObject转换为更特殊的对象类型并不会保证成功,所以它会返回一个可选值。而你需通过检查该值的类型来确认转换是否成功。

//Swift
let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey(“LastRefreshDate”)
if let date = lastRefreshDate as? NSDate {
println(“\(date.timeIntervalSinceReferenceDate)”)
}

当然,如果你能确定这个对象的类型(并且确定不是nil),你可以添加as操作符强制调用。

//Swift
let myDate = lastRefreshDate as NSDate
let timeInterval = myDate.timeIntervalSinceReferenceDate

使用nil(Working with nil)

在Objective-C中,对象的引用可以是值为NULL的原始指针(同样也是Objective-C中的nil)。而在Swift中,所有的值–包括结构体与对象的引用都被保证为非空。作为替代,你将这个可以为空的值包装为optional type。当你需要宣告值为空时,你需要使用nil。你可以在Optionals中了解更多。

因为Objective-C不能保证一个对象是non-nil的,所以Swift在引入Objective-C的API的时候,确保了所有函数的返回类型与参数类型都是optional。在使用Objective-C的API之前,你应该检查并保证该值非空。在某些情况下,你可能绝对确认某些Objective-C方法或者属性永远不应该返回一个nil的对象引用。为了让对象在这种情况下更加易用,Swift使用implicitly unwrapped optionals方法引入对象,implicitly unwrapped optionals 包含optional类型的所有安全特性。此外,你可以直接访问对象的值而无需检查nil或者自己展开它。当你访问这种类型的变量时,implicitly unwrapped optional 首先检查这个对象的值是否不存在,如果值不存在,那它将会抛出一个运行时错误。因此,你通常需要检查和展开一个implicitly unwrapped optional,除非你确定值不会为空。

扩展(Extensions)

Swift的扩展和Objective-C的类别(Category)相似。扩展为原有的类、结构和枚举丰富了功能,包括在Objective-C中定义过的。你可以为系统的框架或者你自己的类型定义扩展。简单要导入合适的模块并且保证你在Objective-C中使用的类、结构或枚举拥有相同的名字。举个例子,你可以扩展UIBezierPath类通过正三角形来创建一个简单的Bézier路径,这个方法只需提供三角形的边长与起点。

//Swift
extension UIBezierPath {
convenience init(triangleSideLength: Float, origin: CGPoint) {
self.init()
let squareRoot = Float(sqrt(3))
let altitude = (squareRoot * triangleSideLength) / 2
moveToPoint(origin)
addLineToPoint(CGPoint(triangleSideLength, origin.x))
addLineToPoint(CGPoint(triangleSideLength / 2, altitude))
closePath()
}
}

你也可以使用扩展来增加属性(包括类与静态属性)。然而,这些属性必须是通过计算才能获取的,扩展不会为类,结构体以及枚举添加存储属性。下面这个例子为CGRect类增加了一个经计算过的area属性。

//Swift
extension CGRect {
var area: CGFloat {
return width * height
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
let area = rect.area
// area: CGFloat = 500.0

你同样可以使用扩展来为类添加协议而无需对它进行子类化。如果这个协议是在Swift中被定义的,你可以添加comformance到它的结构或枚举中,无论它们是在Objective-C或在Swift中被定义。你不能使用扩展来覆盖Objective-C类型中存在的方法与属性。

闭包(Closures)

Objective-C中的blocks会被自动导入为Swift中的闭包。例如,下面是一个Objective-C 中的block变量:

//Objective-C
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {/* … */}

而它在Swift中的形式为:

//Swift
let completionBlock: (NSData, NSError) -> Void = {data, error in /* … */}

Swift的闭包与Objective-C中的blocks能够兼容,所以你可以把一个Swift闭包传递给一个把block作为参数的Objective-C方法。Swift闭包与函数具有相同的类型,所以你甚至可以传递Swift函数的名字。闭包与blocks语义上想通,但在一个地方不同:变量是可以直接改变的,但不是像block那样会拷贝变量。换句话说,Swift中变量的默认行为与Objective-C中__block变量一致。

对象比较(Object Comparison)

如果你在Swift比较两个对象,那么会有两种不同的类型的比较。第一个equality 相等(==)用于比较对象的内容,第二个identity 恒等(===)用以决定常量或者变量是否引用同一个对象实例。在Swift中比较Swift和Objective-C对象使用==和===运算符。

Swift为源自NSObject类的对象提供了默认的==实现。在该运算符的实现中,Swift调用了NSObject类的isEqual:方法。NSObject类仅实现identity (===)比较,所以你应该在源自NSObject的类中实现你自己的isEqual:方法。

由于你可以把Swift对象(包括不是源自NSObject的对象)传递给Objective-C API,所以如果你想要Objective-C API比较对象的内容时,你应该实现isEqual:方法。作为实现类相等的一部分,要确保根据Object comparison中的规则实现hash属性。更进一步说,如果你想要在字典中把类用作键,那么也要遵照Hashable协议,并实现hashValue属性。

Swift类型兼容性(Swift Type Compatibility)

当你定义了一个继承自NSObject或者其他Objective-C 类的Swift类,这些类会自动兼容Objective-C。所有的步骤都有Swift编译器自动完成,如果你从未在Objective-C代码中导入Swift 类,你也不需要担心类型适配问题。另外一种情况,如果你的Swift类并不来源自Objectve-C类,而且你希望能在Objecive-C的代码中使用它,你可以使用下面描述的@objc属性。

@objc可以让你的Swift API在Objective-C和Objective-C runtime中使用。换句话说,你可以在任何Swift方法、类、属性前添加@objc,来使得他们可以在Objective-C代码中使用。如果你的类继承自Objective-C,编译器会自动帮助你完成这一步。编译器还会在类的所有方法和属性前加@objc,如果这个类自己前面加上了@objc关键字。当你使用@IBOutlet,@IBAction,或者是@NSManaged属性时,@objc也会添加在前面。当你使用selector实现target-action设计模式时,这个属性也会非常有用,例如,NSTimer或者UIButton。

当你在Objective-C中使用Swift API,编译器基本对语句做直接的翻译。例如,Swift API func playSong(name: String)(name:String)在Objective-C会被解释为- (void)playSong:(NSString *)name。然而,有一个例外:当在Objective-C中使用Swift的初始化函数,编译器会在方法前添加”initWith”,并且将原初始化函数的第一个参数首字母大写。

例如,这个Swift初始化函数init (songName: String, artist: String将被翻译为- (instancetype)initWithSongName:(NSString*)songName artist:(NSString *)artist 。 Swift同时也提供了一个@objc关键字的变体,允许你为Objective-C中的符号指定名称。例如,如果你的Swift 类的名字包含Objecytive-C中不支持的字符,你就可以为Objective-C提供一个可供替代的名字。如果你给Swift函数提供一个Objcetiv-C名字,请使用Objective-C selector syntax。要记得为带参数的函数添加(:)

//Swift
@objc(Squirrel)
class Белка {
@objc(initWithName:)
init (имя: String) { /*…*/ }
@objc(hideNuts:inTree:)
func прячьОрехи(Int, вДереве: Дерево) { /*…*/ }
}

当你在Swift类中使用@objc(<#name#>)属性,这个类可以不需要命名空间即可在Objective-C中使用。这个属性在你迁徙Objecive-C代码到Swift时同样也非常有用。由于归档过的对象存贮了类的名字,你应该使用@objc(<#name#>)来指定和Objective-C类一样的名字,这样旧的归档可以在新Swift类中恢复。

Objective-C选择器(Selectors)

Objective-Cselector是指向一个Objective-C方法名的类型。在Swift中,Objective-C selector被Selector结构体替代。你可以通过字符串字面量创建一个选择器,比如let mySelector: Selector =”tappedButton:”。因为字符串字面量能够自动转换为选择器,所以你可以把字符串字面量直接传递给任何接受选择器的方法。

//Swift
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))

init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibName, bundle: nibBundle)
myButton.targetForAction(“tappedButton:”, withSender: self)
}

func tappedButton(sender: UIButton!) {
println(“tapped button”)
}
}

注意:performSelector:方法和相关的调用选择器的方法没有导入到Swift中,因为它们是不安全的。

如果你的Swift类继承自Objective-C的类,那么该类的所有方法和属性都可以用作Objective-C的选择器。另外,如果你的Swift类不是继承自Objective-C,如果你想要当选择器来使用你就需要在前面添加@objc关键字,详情请看Swift Type Compatibility。

Swift与Objective-C API交互(三)

Swift类型兼容性

定义一个继承自NSObject或者其他Objective-C的类,它自动与Objective-C兼容。如果你不需要将Swift对象导入Objective-C代码的话,没必要关注类型的兼容性。但是如果在Swift中定义的类不是Objective-C类的子类,在Objective-C中使用的时候,需要用@objc进行说明。

@objc使得Swift的API可以在Objective-C和它的运行时中使用。当使用@IBOutlet@IBAction或者@NSManaged等属性时,自动添加@objc属性。

@objc还可以用来指定Swift中的属性或方法在Objective-C中的名字,比如Swift支持Unicode名字,包括使用中文等Objective-C不兼容的字符。还有给Swift中定义的函数指定一个Selectorde名字。

@objc(Squirrel)
class 长沙戴维营教育 {
    @objc(hideNuts:inTree:)
    func 欢迎光临(Int, 姓名: String) {
        /* ... */
    }
}

@objc(<#name#>)属性作用在Swift的类上时,这个类在Objective-C的使用不受命名空间的限制。同样,在Swift中解归档Objective-C归档的对象时,由于归档对象中存放有类名,因此需要在Swift中用@objc<#name>说明Objective-C的类名。

Objective-C选择器(Selector)

Objective-C的选择器是方法的一个引用。在Swift中对应的是Selector结构体。使用字符串字面量可以构建一个选择器对象,如let mySelector: Selector = "tappedButton:"。由于字符串字面常量可以自动转换为选择器对象,因此可以在任何需要传递选择器的地方使用字符串字面常量。

import UIKit
class MyViewController: UIViewController {
    let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))

    init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!)
    {
        super.init(nibName: nibName, bundle: nibBundle)
        myButton.targetForAction("tappedButton:", withSender: self)
    }

    func tappedButton(sender: UIButton!) {
        println("tapped button")
    }
}
提示
performSelector:以及相关的调用选择器的方法没有被引入到Swfit中来,因为它们不是完全安全的。

如果Swift类继承自Objective-C的类,则它里面的方法和属性都能够作为Objective-C的选择器使用。而如果不是Objective-C的子类,需要使用@objc属性修饰,这个在前面的Swift类型兼容性中有描述。

Swift与Objective-C API交互(二)

兼容id类型

Swift包含一个叫AnyObject的协议,与Objective-C中的id类似,可以表示任意类型的对象。AnyObject协议允许你在利用无类型对象的灵活性的同时保持类型安全。
你可以给AnyObject类型的变量赋任意的值:

var myObject: AnyObject = NSData()

可以直接方法AnyObject类型对象的任意属性和方法,而不需要进行强制类型转换。

let dateDescription = myObject.description
let timeSinceNow = myObject.timeIntervalSinceNow

由于AnyObject类型的变量的类型需要到运行时才能确定,因此可能会导致不安全的代码。特别是你可以访问一个不存在的属性或者方法,它只是在运行时报错。

myObject.characterAtIndex(5)
//crash, myObject doesn't respond to that method

在进行类型转换的时候,不一定转换成功,因此Swift返回的是一个Optional值。你可以检查是否转换成功。

let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefault.objectForKey("LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
    println("\(date.timeIntervalSinceReferenceDate)")
}

如果你能够确定对象的类型,并且不为nil,可以使用as操作符进行强制转换。

let myDate = lastRefreshDate as NSDate
let timeInterval = myDate.timeIntervalSinceReferenceDate

nil对象

Objective-C中使用nil来表示引用一个空对象(null)。Swift中所有的值都不会为nil。如果需要表示一个缺失的值,可以使用Optional。

由于Objective-C不能确保所有值都非空,因此Swift将Objective-C中引入的方法的参数和返回值都用Optional表示。在使用Objective-C对象之前,应该检查它们是否存在。

扩展

Swift的扩展与Objective-C的类别有点类似。扩展能够为已有类、结构体、枚举等增加行为。

下面是给UIBezierPath添加扩展:

extension UIBezierPath {
    class func bezierPathWithTriangle(length: Float, origin: CGPoint) -> UIBezierPath {
        let squareRoot = Float(sqrt(3))
        let altitude = (squareRoot * length) / 2
        let myPath = UIBezierPath()

        myPath.moveToPoint(orgin)
        myPath.addLineToPoint(CGPoint(length, origin.x))
        myPath.addLineToPoint(CGPoint(length / 2, altitude))
        myPath.closePath()

        return myPath
    }
}

可以使用扩展增加属性(包括类属性或静态属性)。不过这些属性只能是通过计算得来,而不能进行存储。下面给CGRect添加一个area属性:

extension CGRect {
    var area: CGFloat {
        return width * height
    }
}

let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
let area = rect.area
//area: CGFloat = 500.0

使用扩展可以在不创建子类的情况下让现有的类响应某个协议。需要注意的是,扩展不能覆盖已有的方法和属性。

闭包

Objective-C中的Block 被自动导入为Swift的闭包。例如:

void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {
    /* ... */
}

在Swift 中对应为:

let completionBlock: (NSData, NSError) -> void = {
    data, error in /* ... */
}

Swift的闭包和Objective-C中的Block是兼容的,可以在Swift中给Objective-C的方法传递闭包来代替Block对象。

闭包和Block对象有一点不同,里面的变量是可变的,也就是说与__block修饰的变量行为相同。

对象比较

Swift中有两种对象比较的方式。第一种是相等(==(equality),用来比较两个对象的内容是否相同。第二种是恒等(===(identity),比较两个变量或者常量是否引用同一个对象。

NSObject只能比较是否引用了同一个对象(恒等),如果要比较内容是否相同,应该实现isEqual:方法。

Swift与Objective-C API交互(一)

Swift和Objective-C可以进行互操作,也就是说可以在Objective-C项目中使用Swift代码,反过来也可以。当然,这种互操作之间最重要的是可以在Swift中调用Objective-C的接口,毕竟目前绝大部分接口都是通过Objective-C提供的。

初始化

在Swift中实例化一个Objective-C的类时,可以用Swift语法调用它的一个初始化器。Objective-C的初始化方法被分割成关键字。例如“initWith”变成“init”,而剩下的部分作为方法的参数名。

Objective-C的代码:

UITableView *myTableView = [[UITableView alloc] initWithFrame: CGRectZero style: UITableViewStyleGrouped];

对应的Swift代码为:

let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)

在Swift中不需要调用alloc方法,它会自动处理对象的创建功能。注意:Swift不会显式的调用init方法。

定义变量或者常量的时候,可以省略它的类型,Swift会自动识别。

let myTextField = UITextField(frame: CGRect(0.0, 0.0, 200.0, 40.0))

为了方便起见,Objective-C的工厂方法被映射为Swift中的初始化器。例如:

UIColor *color = [UIColor colorWithRed: 0.5 green: 0.0 blue: 0.5 alpha: 1.0];

转换为Swift:

let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)

属性访问

在Objective-C和Swift中访问属性都是使用点操作符。

myTextField.textColor = UIColor.darkGrayColor()
myTextField.text = "Hello world"
if myTextField.editing {
    myTextField.editing = false
}

访问和设置属性的时候不需要使用圆括号,上面darkGrayColor之所以有括号,是因为调用的是UIColor的类方法,而不是属性。

Objective-C中不需要参数并且有一个返回值的方法,可以被当作隐含的获取方法(getter),从而使用点操作符。但是在Swift中点操作符只能访问Objective-C中使用@property定义的属性。

方法调用

在Swift中使用点操作符调用Objective-C中的方法。

Objective-C的方法在Swift中调用的时候,它的第一部分成为Swift的基本方法出现在括号之前。然后函数的第一个参数没有名字,剩下的部分作为Swift函数对应的参数名称。
Objective-C语法:

[myTableView insertSubview: mySubview atIndex: 2];

Swift代码:

myTableView.insertSubview(mySubview atIndex: 2)

调用无参的方法:

myTableView.layoutIfNeeded()

Swift语言编程实战系列教程(四)—从swift调用C函数

转自CC(CocoaChina)论坛上jamszhy的帖子

本节课的主要目的是熟悉从swift调用C函数。

本系列教程旨在通过简单、易懂、有趣的程序让大家熟悉swift语法,并且学会编程

我们发现swift语言目前没有提供输入语句,那我们可以让它调用C语言的输入函数就可以解决。

1. 新建一个项目

在Choose a template for your new project对话框中,点击左边OS X下的Application,再选择Command Line Tool,然后点击“ Next ”。

2. 给项目增加一个桥接文件:

使用“ New File… ”来创建文件,在Choose a template for your new file对话框中,点击左边OS X下的Source,再选择Objective-C File,然后点击“ Next ”。

输入文件名,点击“ Create ”,Xcode会弹出“ Would you like to configure an Objective-C bridging header? ”问题对话,点击“ Yes ”,Xcode会自动造成一个桥接头文件(假设名称为bridging-header.h)。

3. 然后再创建一个C语言的文件

使用“ New File… ”来创建文件,在Choose a template for your new file对话框中,点击左边OS X下的Source,再选择C File,然后点击“ Next ”。

输入文件名(假设文件名为CFile.c),点击“ Create 。

4. 然后在桥接文件中声明C语言的函数

在刚才自动生成的桥接头文件(参考bridging-header.h)中声明C语言函数,比如:
void youCanCallMeFromSomeWhere();

5. 然后在C文件中定义你的C函数

在刚才创建的C File(参考CFile.c)文件中定义之前在桥接头文件中声明的C函数,内容比如:
void youCanCallMeFromSomeWhere() {
printf(” Yeah, you can reach me now.\n “);
}

6. 最后在main.swift文件中就可以直接调用C函数了,比如用下面的代码:

println(“正在测试从Swift语言中调用C语言函数…”)

youCanCallMeFromSomeWhere()

Swift语言编程实战系列教程(三)—一颗爱你的心

转自CC(CocoaChina)论坛上jamszhy的帖子

本节课的主要目的是熟悉循环的概念及循环语句的使用。
一颗爱你的心,时时刻刻为你跳不停!需要使用循环语句!

//
//  main.swift
//  jumpHeart
//
//  Created by jamszhy on 14/6/6.
//  Copyright (c) 2014年 jamszhy. All rights reserved.
//  weibo.com/jamszhy

import Foundation

//一颗跳动心,可以看成不同大小的心不停的在屏幕上出现
//由于需要重复跳动,那我们需要使用循环
//先用最简单的循环:死循环

while true {
print(“\n\n”)
print(”                           \n”)
print(”                          \n”)
print(”                          \n”)
print(”                          \n”)
print(”                         \n”)
print(”           *****  \n”)
print(”            ***  \n”)
print(”             *  \n”)
print(”                      \n”)
print(”                     \n”)
print(”                     \n”)
print(”                     \n”)
print(”               \n”)
print(”                \n”)
usleep(100000)
system(“clear screen”)

print(“\n\n”)
print(”                           \n”)
print(”                \n”)
print(”                   \n”)
print(”              \n”)
print(”          ******  \n”)
print(”         ********  \n”)
print(”          ******  \n”)
print(”           ****  \n”)
print(”            **  \n”)
print(”       \n”)
print(”            \n”)
print(”             \n”)
print(”               \n”)
print(”                \n”)
usleep(100000)
system(“clear screen”)

print(“\n\n”)
print(”                           \n”)
print(”                \n”)
print(”                   \n”)
print(”       **** *****  \n”)
print(”      ************  \n”)
print(”       **********  \n”)
print(”        ********  \n”)
print(”         *****  \n”)
print(”          ***  \n”)
print(”           *  \n”)
print(”            \n”)
print(”             \n”)
print(”               \n”)
print(”                \n”)
usleep(100000)
system(“clear screen”)

print(“\n\n”)
print(”                           \n”)
print(”                \n”)
print(”       ***** *****  \n”)
print(”     ***************  \n”)
print(”    *****************  \n”)
print(”     ***************  \n”)
print(”      *************  \n”)
print(”        *********  \n”)
print(”          *****  \n”)
print(”           ***  \n”)
print(”            *    \n”)
print(”             \n”)
print(”               \n”)
print(”                \n”)
usleep(100000)
system(“clear screen”)

print(“\n\n”)
print(”                           \n”)
print(”      ******   ******  \n”)
print(”    ********* *********  \n”)
print(”   **********************  \n”)
print(”  ************************  \n”)
print(”   **********************  \n”)
print(”    *******************  \n”)
print(”      ****************  \n”)
print(”        *************  \n”)
print(”          *********  \n”)
print(”            *****  \n”)
print(”             ***  \n”)
print(”              *   \n”)
print(”                \n”)
usleep(100000)
system(“clear screen”)

print(“\n\n”)
print(”      ******       ******   \n”)
print(”    **********   **********  \n”)
print(”  ************* *************  \n”)
print(” *****************************  \n”)
print(“*******************************  \n”)
print(” *****************************  \n”)
print(”  ***************************  \n”)
print(”    ***********************  \n”)
print(”      *******************  \n”)
print(”        ***************  \n”)
print(”          ***********  \n”)
print(”           *******  \n”)
print(”              ***  \n”)
print(”               *  \n”)
usleep(100000)
system(“clear screen”)
}

//我们现在想控制循环次数
//那么需要定义变量:
var num = 0
while num < 10 {
//重复执行的语句
num++
}

//for循环的两种格式
for num in 0..10 {
//重复执行的语句
}
for num = 0; num < 10; num++ {
//重复执行的语句
}

//do while格式
do
{
//重复执行的语句
num++

}while num < 10
本系列教程旨在通过简单、易懂、有趣的程序让大家熟悉swift语法,
并且学会编程

Swift语言编程实战系列教程(二)—女朋友数玫瑰

转自CC(CocoaChina)论坛上jamszhy的帖子

本系列教程旨在通过简单、易懂、有趣的程序让大家熟悉swift语法,
并且学会编程。
代码很长,但思路简单,适合初学者!后面的课程我们会使用循环和函数的知识来重写。到时你就会看到代码很简短!

//
//  main.swift
//  countRose
//
//  Created by jamszhy on 14/6/6.
//  Copyright (c) 2014年 jamszhy. All rights reserved.
//  weibo.com/jamszhy

import Foundation

println(“Hello, World!”)

//女朋友数玫瑰花的动画
//1.先清屏
system(“clear screen”)
println(” 我送你18朵玫瑰花,”)
sleep(1)
println(” 你开始数:”)
sleep(1)
//2.然后不停的拍照,让照片一次在屏幕上放
println(“{@}”)
println(” | “)
println(“\\|/”)
println(” | “)
println(” |”)
println(” 1″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}”)
println(” |  | “)
println(“\\|/\\|/”)
println(” |  |  “)
println(” |  |  “)
println(”    2″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}”)
println(” |  |  | “)
println(“\\|/\\|/\\|/”)
println(” |  |  |  “)
println(” |  |  |  “)
println(”       3″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}”)
println(” |  |  |  | “)
println(“\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  “)
println(” |  |  |  |  “)
println(”          4 “)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}”)
println(” |  |  |  |  | “)
println(“\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  | “)
println(” |  |  |  |  | “)
println(”             5″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  | “)
println(” |  |  |  |  |  |  “)
println(”                6 “)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  | “)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  | “)
println(”                   7 “)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备
println(“{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  | “)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  | “)
println(” |  |  |  |  |  |  |  | “)
println(”                      8 “)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  | “)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  | “)
println(” |  |  |  |  |  |  |  |  | “)
println(”                         9″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备
println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  | “)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |”)
println(”                            10″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |”)
println(”                               11″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备
println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                  12″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                     13″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备
println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                        14″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                           15″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                              16″)
sleep(1)  //3.然后控制照片之间的切换速度
system(“clear screen”) //4.清楚屏幕,为新照片放映做准备

println(“{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}{@}”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(“\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/\\|/”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(” |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |”)
println(”                                                 17″)
sleep(1)  //3.然后控制照片之间的切换速度

println(” 你数来数去,”)
sleep(1)
println(” 只有17朵!”)
sleep(1)
println(” 因为,”)
sleep(1)
println(” 还有一朵,”)
sleep(1)
println(” 就是你!!!!”)
sleep(1)
println(” 你是我的玫瑰,你是我的花,”)
sleep(1)
println(” 你是我的爱人,是我的牵挂!!!”)

Swift语言编程实战系列教程(一)— 给女朋友写情书

转自CC(CocoaChina)论坛上jamszhy的帖子

本节课的主要目的是熟悉swift语言的输出函数:println与print
本系列教程旨在通过简单、易懂、有趣的程序让大家熟悉swift语法,
并且学会编程。
你是风,你是雨,娶到你我就能呼风唤雨!
你是天,你是地,娶到你我就能顶天立地!
你是财,你是宝,娶到你我就能招财进宝!
//
//  main.swift
//  girlfriendCountRose
//
//  Created by jamszhy on 14/6/6.
//  Copyright (c) 2014 jamszhy. All rights reserved.
//  weibo.com/jamszhy

import Foundation

println(“Hello, World!”)
println(“Hello Swift, nice to meet you!”)

//给女朋友写一封情书:
println(你是风,你是雨,娶到你我就能呼风唤雨!)
println(你是天,你是地,娶到你我就能顶天立地!)
println(你是财,你是宝,娶到你我就能招财进宝!)

//给女朋友送一朵玫瑰花
println(“{@}”)
println(” |”)
println(“\\|/”)
println(” |”)
println(” |”)

//printprintln的区别:
//print不自动加\n

print(“Hello, World!\n”)
print(“Hello Swift, nice to meet you!\n”)

//给女朋友写一封情书:
print(你是风,你是雨,娶到你我就能呼风唤雨!\n”)
print(你是天,你是地,娶到你我就能顶天立地!\n”)
print(你是财,你是宝,娶到你我就能招财进宝!\n”)

//给女朋友送一朵玫瑰花
print(“{@}  \n”)
print(” |   \n”)
print(“\\|/ \n”)
print(” |   \n”)
print(” |   \n”)

Swift中文教程(12)下标

转载自letsswift.com

类,结构和枚举类型都可以通过定义下标来访问一组或者一个序列中的成员元素。通过下标索引就可以方便地检索和设置相应的值,而不需要其他的额外操作。比如你可以通过someArray[index]来访问数组中的元素,或者someDictionary[key]来对字典进行索引。

你可以为一个类型定义多个下标,以及适当的下标重载用来根据传递给下标的索引来设置相应的值。下标不仅可以定义为一维的,还可以根据需要定义为多维的,多个参数的。

1、下标语法

下标可以让你通过实例名后加中括号内一个或多个数值的形式检索一个元素。语法和方法语法和属性语法类似,通过使用subscript关键定义,一个或多个输入参数以及一个返回值。不同于实例方法的是,下标可以是可读写的或者只读的。这种行为通过一个getter和setter语句联通,就像是计算属性一样。

12345678
subscript(index:Int)->Int{
    get{
        // return an appropriate subscript value here
    }
    set(newValue){
        // perform a suitable setting action here
    }}

newValue的类型和下标返回的类型一样。和计算属性一样,你可以选择不指定setter的参数,因为当你不指定的时候,默认参数newValue会被提供给setter。

和计算属性一样,只读下标可以不需要get关键词:

123
subscript(index:Int)->Int{
    // return an appropriate subscript value here}

下面是一个只读下标的实现,定义了一个TimesTable结构来表示一个整数的倍数表:

123456789
structTimesTable{
    let multiplier:Int
    subscript(index:Int)->Int{
        return multiplier * index
    }}
let threeTimesTable =TimesTable(multiplier:3)
println("six times three is \(threeTimesTable[6])")// prints "six times three is 18"

在这个例子中,实例TimesTable被创建为3倍数表,这是通过在初始化的时候为multiplier参数传入的数值3设置的。

注意:

倍数表是根据特定的数学规则设置的,所以不应该为threeTimeTable[someIndex]元素设置一个新值,所以TimesTable的下标定义为只读。

 

2、下标的使用

下标的具体含义由使用它时的上下文来确定。下标主要用来作为集合,列表和序列的元素快捷方式。你可以自由的为你的类或者结构定义你所需要的下标。

比如说,Swift中字典类型实现的下标是设置和检索字典实例中的值。可以通过分别给出下标中的关键词和值来设置多个值,也可以通过下标来设置单个字典的值:

12
var numberOfLegs =["spider":8,"ant":6,"cat":4]
numberOfLegs["bird"]=2

上面的例子中定义了一个变量numberOfLegs,然后通过键值对初始化。numberOfLegs的类型是字典类型Dictionary<String, Int>。在字典创建之后,例子使用了下标赋值方法添加了一个类型为字符串的键”bird”和Int值2到字典中。

更多关于字典的下标可以参考:访问和修改字典这一章节

注意:

Swift中字典类型实现的键值对下标是可选类型。对于numberOfLges字典来说,返回的值是Int?,也就是可选Int值。字典的这种使用可选类型下标的方式说明不是所有的键都有对应的值。同样也可以通过给键赋值nil来删除这个键。

 

3、下标选项

下标可以接收任意数量的参数,参数的类型也可以各异。下标还可以返回任何类型的值。下标可以使用变量参数或者可变参数,但是不能够使用输入输出参数或者提供默认参数的值。

类或者结构可以根据需要实现各种下标方式,可以在需要的时候使用合适的下标通过中括号中的参数返回需要的值。这种多下标的定义被称作下标重载。

当然,最常见的下标用法是单个参数,也可以定义多个参数的下标。下面的例子演示了一个矩阵Matrix结构,它含有二维的Double值。矩阵结构的下标包括两个整形参数:

12345678910111213141516171819202122
structMatrix{
    let rows:Int, columns:Int
    var grid:Double[]
    init(rows:Int, columns:Int){
        self.rows = rows
        self.columns = columns
        grid =Array(count: rows * columns, repeatedValue:0.0)
    }
    func indexIsValidForRow(row:Int, column:Int)->Bool{
        return row >=0&& row < rows && column >=0&& column < columns
    }
    subscript(row:Int, column:Int)->Double{
        get{
            assert(indexIsValidForRow(row, column: column),"Index out of range")
            return grid[(row * columns)+ column]
        }
        set{
            assert(indexIsValidForRow(row, column: column),"Index out of range")
            grid[(row * columns)+ column]= newValue
        }
    }}

矩阵Matrix提供了一个初始化方法,使用两个参数rows和columns,然后建立了一个数组来存储类型为Double的值rows*columns。每个矩阵中的位置都被设置了一个初始值0.0。通过传递初始值0.0和数组长度给数组初始化方法完成上述操作。数组的初始化方法在:创建和初始化数组中有更详细的叙述。

你可以传递两个参数row和column来完成Matrix的初始化:

1
var matrix =Matrix(rows:2, columns:2)

上面的初始化操作创建了一个两行两列的矩阵Matrix实例。这个矩阵实例的grid数组看起来是平坦的,但是实际上是矩阵从左上到右下的一维存储形式。

image 矩阵中的值可以通过使用包含row和column以及逗号的下标来设置:

12
matrix[0,1]=1.5
matrix[1,0]=3.2

这两个语句调用了下标的setter方法为右上和左下角的两个元素分别赋值1.5和3.2

image

矩阵下标的getter和setter方法都包括了一个断言语句来检查下标row和column是否有效。通过indexIsValid方法来判断row和column是否在矩阵的范围内:

123
func indexIsValidForRow(row:Int, column:Int)->Bool{
    return row >=0&& row < rows && column >=0&& column < columns
}

如果访问的矩阵越界的时候,断言就会被触发:

12
let someValue = matrix[2,2]// this triggers an assert, because [2, 2] is outside of the matrix bounds

感谢翻译小组成员:李起攀(微博)、若晨(微博)、YAO、粽子、山有木兮木有枝、渺-Bessie、墨离、矮人王、CXH、Tiger大顾(微博)
个人转载请注明出处和原始链接,商业转载请联系我们~ 感谢您对我们工作的支持~