说起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 是不能共存的.
所以说在定义属性前还是要想好属性是做什么用的