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。

iOS 8隐藏功能曝光,支持文本编辑,预览、Siri API等

苹果自从库克接手后,做出了很多改变。如今又有开发者在iOS 8的中发现了之前没有被介绍过的新功能,用户可以更改主题的颜色以及更换字体。苹果或为iOS增加更多的个性化设置。

开发者发现iOS 8中可更改主题颜色和字体(图片来自cnbeta)

通过iOS 8的发布,我们可以看到苹果确实变得更加开放,之前在国内一直饱受诟病的输入法终于得到了解决,允许安装第三方输入法键盘。不过按照苹果以前的惯例,在iOS测试版中存在的一些新功能有着实验性质,在正式版会被取消。

如果对这个新功能感兴趣的用户,只要把iOS 8的UIUseAlternateUI设置为Enable就可以了。

另外,据一位 iOS 开发者在推特上表示,发现了iOS 8测试版中苹果正在测试独立 iTunes 广播应用的证据。此外,苹果似乎也正在测试 iOS 版文本编辑和预览应用。当然,一起被发现的还有 Siri API。这些发现与之前的传言相同,消息称苹果将在 iOS 8系统中增加文本编辑、预览和 iTunes 广播等新应用。当然,这些新功能很有可能被推迟至 iOS 8.1系统中。

我们可以在 Continuity 功能相关的.plist 文件中找到 com.apple.Preview和 com.apple.TextEdit 等代码。对于 Siri 控制其他应用,开发者发现了 UIApplicationLaunchOptionsSiriTaskKey 这样的字符串。最有趣的是,开发者还发现了 Healthbook 字符串。在 WWDC 发布会之前,有消息称苹果将在 iOS 8中预装 Healthbook 应用,这款主打健康和健身数据收集的应用将可以与传言中的 iWatch 互相协作,只是苹果只在发布会上推出了 Health(健康)应用。

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()

iOS 8/Yosemite AirDrop 视频曝光

WWDC 后,围绕着越来越开放的 OS X 和 iOS 系统上,果粉总是有聊不完的话题。演示上那幕 Mac 和 iOS 8 间的 AirDrop 帅爆了。或许它们之间的无缝沟通就成了很多用户在决定是否购买 Mac 上的一个巨型砝码。之前爱应用小编也分别刷了 iOS 8 和 Yosemite,但未实验成功,必须要在 Mac 重装成一台新机时才可能使用。不过并不是所有人都愿意为了看看这个功能而把自己的电脑清掉,今天就跟着 AppleInsider的视频来抢先看看这个超实用功能吧!

http://v.youku.com/v_show/id_XNzIzODgyNzky.html?firsttime=95

目前,咱们只能在 iOS 设备间或是 Mac 间通过 AirDrop 来传输图片等文件和文件夹。视频中,使用方法也与之前很相似。在 iOS 端和 Mac 端都打开 AirDrop,两者就能在 AirDrop 界面上互相找到,之后通过拖拽即可完成文件传输。从 iPhone 上传来的照片会被默认储存在下载文件夹中。功能这么屌,能说服自己下一台电脑换 Mac 咩?

iOS 8将支持多种模式的多任务功能

在前不久的 2014 年 WWDC 上,苹果推出了自己最新的 iOS 系统 iOS 8,但传言中的分屏多任务管理功能却并没有出现。最近,有开发者发现,iOS 8 的分屏多任务管理功能是的确存在的,并且向用户提供了多种分屏模式。开发者发现 iOS 8 系统的 SpringBoard 包含了允许应用在多种尺寸下同屏运行的代码,两个应用可以在 1/4、1/2 或 3/4 的比例下运行。

iOS 8 支持分屏多任务的消息早在今年 5 月就曾出现,消息称 iPad Air 将支持两个应用同时运行,就像是微软 Surface。iOS 8 分屏多任务功能只支持在横屏模式下运行,两个同时运行的应用可以互相分享链接、图片和信息等数据。目前的消息是,多屏多任务可能只支持 iPad Air,随后也有可能支持 iPhone 和 Retina iPad mini。此外,分屏多任务功能还可能支持苹果没有发布的 12.9 英寸 iPad Pro。WWDC 举行之前,《纽约时报》就有报道称分屏多任务功能正在开发中,但不会在 WWDC 上公布。

Source:macrumors

 

数据说话4G版本iPhone约占全球LTE 42%

注意,注意,说的是硬件4G版iPhone,我们知道,并不是所有的人在4G设备上使用4G卡,所以不用想象,全球有这么多。。。很多中国移动还在4G设备上使用2G语音卡呢。

根据市场研究机构 Counterpoint Research 最新的研究报告显示,在过去的一年时间里,4G 版本 iPhone 约占全球 LTE 手机总量的 42%,而全球的 LTE 手机出货量增长了 91%,约占全球智能手机出货量的四分之一。

从图中,我们可以看出,无论是 2013 年第一季度还是 2014 年第一季度,苹果的 LTE 手机出货量均遥遥领先三星。在 2014 年第一季度有 2% 的下降,可能和迟迟未推出 iPhone 6 有一定关系。而这份数据并未考虑连接到 LTE 网络的平板设备,若是把 LTE 版本的 iPad 算上,苹果的比例还会提高。目前美国约占世界 LTE 手机的三分之一,排名第一,中国已经排名世界第三位,并有可能在今年超越排名第二的日本。

目前中国的 4G 市场,iPhone 设备接入 4G 网络的主要还是移动用户,中国移动在 2013 年底就已开展 4G 网络的试运营,过去很多忍受移动 2G 龟速网络的 iPhone 用户,趁此机会用上了移动的 4G 网络。同时中国联通和中国电信也在积极争取 FDD-LTE 的牌照,若是牌照拿到,体验到 4G 的用户还将会进一步增加。有体验到 4G 网络的 iPhone 用户吗?来说说看实际的体验速度。

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()