Swift vs Objective-c (2)- Enumerations, lazy, closure, protocol

本文对比Swift和Objective-C在枚举、懒加载属性、闭包和协议方面的差异。Swift的枚举更丰富,支持更多类型;懒加载属性提供延迟初始化;闭包与OC的Block类似但有细微差别,Swift通过unowned或weak避免循环引用;协议方面,Swift允许更详细的声明和扩展,甚至可以为协议方法提供默认实现。

枚举(Enumerations)

Swift的枚举比OC的多了很多内容, OC的枚举就2点:

  • 普通枚举,不带类型(和c语言一样)
  • 带类型枚举 (因为最开始OC出来的时候, c和C++语言还不支持带类型枚举,所以oc打了个补丁来支持这种特性)

普通枚举, 有好几种写法,详细见(http://blog.163.com/redhumor@126/blog/static/1955478420114333815940/

typedef enum _TTGState {
  TTGStateOK  = 0,
  TTGStateError,
  TTGStateUnknow
} TTGState;
//指明枚举类型
TTGState state = TTGStateOK;

带类型枚举, 只支持2种类型,int和option, 所以如果swift的其它类型的enum是转换不到oc的。

//NS_ENUM,定义状态等普通枚举
typedef NS_ENUM(NSUInteger, TTGState) {
  TTGStateOK = 0,
  TTGStateError,
  TTGStateUnknow
};
//NS_OPTIONS,定义选项
typedef NS_OPTIONS(NSUInteger, TTGDirection) {
  TTGDirectionNone = 0,
  TTGDirectionTop = 1 << 0,
  TTGDirectionLeft = 1 << 1,
  TTGDirectionRight = 1 << 2,
  TTGDirectionBottom = 1 << 3
};

Lazy属性

Swift比oc多了一个lazy属性,简单来说,就是当访问到的时候才赋值:

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // 这里会提供数据管理功能
}

如果没有lazy,那么DataImporter在class DataManager初始化的时候就会赋值,但是有了lazy后,只有在importer被访问到的时候才会赋值。
但是,如果一个被标记为lazy的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。

Closure vs Block

Swift 的Closure和OC的block很相似, 但是有些微不同。

打印结果是42, block其实是对值做了一个copy。

    int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };

    anInteger = 84;

    testBlock();

结果是2,closure是做了一个引用

        var first = 1

        let numberClosure = {
            print("\(first)")

        }

        first = 2

        numberClosure()

当oc用了__block 以后,就和swift一样了, 结果是84, 同样用了__block以后,在block里面可以更改变量的值,

    __block int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };

    anInteger = 84;

    testBlock();

如果block或者closure里面不是引用的值类型的变量的话,那么都是用强引用指向外部对象。 所以都有可能会产生循环引用。
那么是如何解决的呢?
OC是用__weak 关键字:

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}

Swift是用unowned或者weak关键字,如果修饰的对象是不会为nil的用unowned, 如果对象可能为nil用weak, 所以用weak修饰的对象应该是一个optional。[unowned self, weak delegate = self.delegate!] 就是所谓的capture list, 放在传入参数的前面。

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}

如果没有传入参数:

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}

Protocol

oc的协议里面可以是属性或者函数:

在.h 文件里面声明:

@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end

在.m 文件里面声明一个变量,conform这个协议:

@interface XYZPieChartView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;
...
@end

协议可以是optional的:

@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end

因为协议是optional的, 所以用的时候要用respondsToSelector检测:

    NSString *thisSegmentTitle;
    if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
        thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
    }

协议也可以继承于其它协议:

@protocol MyProtocol <NSObject>
...
@end

但是,如果想给协议里面的方法提供缺省实现,或者想给协议做一个扩展(category),都是不可以的,只有class才可以做这些。当然,可以下载第3方的库来实现这些功能。
但是值得高兴的是,Swift都是可以做的。

Swift的语法参考见这里
下面是一些比较特别的知识点:
协议要求声明属性到底是不是可读可写的, 协议总是用 var 关键字来声明变量属性,在类型声明后加上 { set get } 来表示属性是可读可写的,可读属性则用 { get } 来表示:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}

如果要定义类类型的属性/方法,相当于OC里面的+, 用static关键字。
如果要在class(类)里面声明一个类属性,可以用static或者class关键字。

protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
}

protocol SomeProtocol {
    static func someTypeMethod()
}

结构struct也可以conform一个协议, 注意struct里面并没有写初始化方法,来对fullName初始化, 如果是class,是会报错的。

struct Person: FullyNamed {
    var fullName: String
}
let john = Person(fullName: "John Appleseed")

如果协议里面的函数会更改当前实例的变量,如果当前的实例是struct或者enum,那么加mutating, 协议要加,当前的实例的声明中也要加。

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case Off, On
    mutating func toggle() {
        switch self {
        case Off:
            self = On
        case On:
            self = Off
        }
    }
}

协议可以要求conform它的实现者,实现初始化函数, 这个初始化函数可以是可失败构造器。init?(xxx)

protocol SomeProtocol {
    init(someParameter: Int)
}

实现者要加require

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // 这里是构造器的实现部分
    }
}

如果SomeClass继承于superClass,那么要加override

protocol SomeProtocol {
    init()
}

class SomeSuperClass {
    init() {
        // 这里是构造器的实现部分
    }
}

class SomeSubClass: SomeSuperClass, SomeProtocol {
    // 因为采纳协议,需要加上 required
    // 因为继承自父类,需要加上 override
    required override init() {
        // 这里是构造器的实现部分
    }
}

协议可以像其他普通类型一样使用,使用场景如下:

  • 作为函数、方法或构造器中的参数类型或返回值类型
  • 作为常量、变量或属性的类型
  • 作为数组、字典或其他容器中的元素类型

这就非常cool了,还有更有用的特性: 协议可以扩展:

protocol TextRepresentable {
    var textualDescription: String { get }
}

extension TextRepresentable {
    var textualDescription: String {
        return "A sided dice"
    }
}

上面的扩展实际上是协议的实现, 类可以conform TextRepresentable,可以直接使用textualDescription, 也可以重新对textualDescription赋值。用上面这种设计模式,就可以用组合的方式而不是继承的方式来实现框架。

而扩展也可以conform协议,这是我经常用的设计模式,这样代码更加清晰。比如: 当界面有一个textField组件的时候,都要写好几个delegete,这时候直接把他们写到扩展里面

extension SnakesAndLadders: PrettyTextRepresentable {
    。。。。。。。
}

在扩展协议的时候,可以指定一些限制条件,例如,你可以扩展 CollectionType 协议,但是只适用于集合中的元素采纳了 TextRepresentable 协议的情况:

extension CollectionType where Generator.Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joinWithSeparator(", ") + "]"
    }
}

可以限定协议只能被类(class)使用,用class关键字,这个特性觉得什么用处:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 这里是类类型专属协议的定义部分
}

可以将2个协议合成一个变量用, 用

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
    print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// 打印 “Happy birthday Malcolm - you're 21!”

wishHappyBirthday这个函数并不关心是哪一个对象调用它,它只关心对象是否遵循了

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值