带我飞5iOS Developer程序员

swift入门_属性的那些坑

2016-02-22  本文已影响1523人  明月钓无痕

说起swift的第一个难点,就是关于属性.对于刚开始接触OC时实例变量/成员变量/属性,傻傻分不清.自从接触到了Swift,才知道那都不是事儿.
下面就来看看Swift的属性是怎么回事.
属性又分为存储属性,计算属性,类属性

1.存储属性

对于存储属性,没有什么好说的.对于熟悉OC的来说是一样的.

// 定义一个类
class Person {
  // 下面的定义的都是属性,而且是存储属性,和OC相同,为了保存属性值
  // 使用var 定义可变属性变量
    var age: Int = 10
  // 使用let 定义不可变的属性常量
    let name: String = "张三"
  // 使用? 表示这个是可选值
 // 上面两个变量必须有初始值,所以在定义变量时进行了赋值
 // height变量由于是可选的,没有必要赋初值
    var height: Double?
}
// 这里定义了一个Person对象
let p = Person()
// 由于age,height都是变量,可以直接通过点语法进项属性赋值
p.age = 5 
p.height = 200
//p.name = "李四" // 由于name是定义的是常量,这里赋值是报错的

2.计算属性

刚才提到的存储属性还是觉得很正常的,但是用习惯了OC,对于习惯了set和get方法的人们来说,刚开始简直是噩梦.这玩意是什么鬼.
下面拿来我之前写过的一个分类来说明一下.
有时候我们为了设置frame的x,y,w,h我们需要一层层点下去,这里写一个分类,为了方便赋值取值.我们直接为View加了个属性.这样子我们通过set和get进行赋值和取值.这个应该很熟悉了

@property (nonatomic, assign) CGFloat x;
@property (nonatomic, assign) CGFloat y;
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat height;

// x
- (void)setX:(CGFloat)x {
    CGRect frame = self.frame;
    frame.origin.x = x;
    self.frame = frame;
}

- (CGFloat)x {
    return self.frame.origin.x;
}

//...写法类似,只去了个

那个类似的功能我们使用swift来看一下,对UIView写了一个扩展.

extension UIView {
    var x: CGFloat {
    // get方法
    // 需要注意的是:写了set,get是必须写的,如果只写get,相当于只读.
        get {
           return self.frame.origin.x
        }
        // set 方法
        set {
            var frame = self.frame
            frame.origin.x = newValue
            self.frame = frame
        }
    }
}

看到Swift的代码,可能会很吃惊,这不是很像吗?对是相同的代码.写法是相同的.但是实际上和OC还是有很大区别的.
注意
1.计算属性的值是不固定的,是计算得来的,所以只能使用var来修饰,不能使用let
2.计算属性本身是不能直接复制的,他是通过其他的变/常量计算结果得到的数据.

// 由于Swift我们没有成员变量,所以我们要避免死循环的问题
// 所以再OC中常用到的_xxx,在Swift中不好用了

class Person {
// 下面由于进行了赋值,其实是一个存储属性,再去写get方法是没意义的.
//    var age: Int = 10 {
//        return 20
//    }
    
  // 这种情况都会是死循环,具体是什么原因,熟悉OC的应该没问题吧
    var age: Int {
        get {
            return age
        }
        
        set {
            age = newValue
        }
    }
}

说道这里我们就得提一下懒加载,在OC中我们的懒加载其实是重写了属性的get方法,在Swift中就要改变一下写法了

// 定义了一个属性
@property (nonatomic, strong) NSMutableArray *topics;

// 懒加载
- (NSMutableArray *)topics
{
    if (!_topics) {
        _topics = [NSMutableArray array];
    }
    return _topics;
}

Swift中的懒加载会使用到关键字lazy

lazy var topics: NSMutableArray? = NSMutableArray()

// 当然如果要在懒加载时做一下初始化配置,那么我们会使用闭包的形式
lazy var topics: NSMutableArray? = {
    // 这里面进行初始化配置
}()

 // 懒加载 ps: 这是从项目中截取的一段,为了说明问题,直接拷贝会报错
    lazy var pictureView: DZTopicPictureView? = {
        let pictureView = DZTopicPictureView.pictureView()
        self.contentView.addSubview(pictureView)
        return pictureView 
    }()

通过这里我们似乎觉的get/set和OC中有了区别了,那么再看看下面一个

3.属性监视器

所谓的属性监视器,就是在属性值的改变前后进行一些操作,这个才是相当于我们的OC中所说的get方法,拿到值后再进行一些操作

class Person {
    var age: Int?  {
        willSet {
            print(newValue) // newValue将要新赋的值5
        }
  // 当然如果觉得不习惯使用newValue/oldValue, 我们可以取一个内部变量名,didSet也是可以的
 //  willSet(age) {
 //           print(age)            
  //      }
        didSet {
            print(oldValue) // oldValue表示旧值 nil
        }
    }    
}

let p = Person()
p.age = 5

注意
1.计算属性有get和set方法去改变属性值,因此监听就就没什么意义了
2.设置默认值时不会调用willSet和didSet,见下面代码.

class Person {
  // 默认值
    var age: Int? = 20  {
        willSet {
            print(newValue) 
        }
        didSet {
            print(oldValue)
        }
    }    
}

3.willSet和didSet set和get 是不能共存的.

所以说在定义属性前还是要想好属性是做什么用的

上一篇下一篇

猜你喜欢

热点阅读