合成属性
(1)使用@property来合成属性。
@property (指示符) 类型 属性名;
@property合成属性之后,一次性得到3个东西:成员变量、setter方法、getter方法。
(2)使用@synthesize指定合成属性对应的成员变量的名字。
合成属性的指示符
assign:如果属性类型是基本类型,就用该指示符。
short、int、long、long long、float、double、BOOL、枚举。
程序只对该属性赋值,不改变引用计数(RC)。
atomic | nonatomic: 对于该属性的赋值操作(setter)是否为原子的。
如果setter是原子的,也就是线程安全的,那么性能较低;反过来,线程不安全,性能就更好。
如果你声明该属性是atomic,那么你去重写setter、getter方法时,就必须自己保证线程安全。
copy:对属性赋值时,程序会先把被赋的值拷贝一个副本。然后用副本进行赋值。
一般来说,如果你被赋的值的类型是可拷贝的,尽量使用该指示符。
getter、setter: 用于改变getter、setter方法的方法名。
getter=方法名 setter=方法名:
readonly|readwrite:readonly表明只生成getter方法,没有setter方法。
readwrite表明同时生成getter、setter方法 —— 这个是默认。
retain:每次赋值,都会将其RC增加1。 ——你们以后基本上不会使用它。主要在MRC(Manual Reference Counting).在ARC(Auto Reference Counting)阶段无需使用该指示符。
weak | strong:弱引用和强引用。
弱引用:一个弱引用不会增加对象的RC。通过弱引用去访问被指向的对象时,可能为nil。
当两个对象形成强引用循环时,就一定要把其中一个引用改为弱引用。
强引用:默认就是strong。
unsafe_unretainted:类似于weak,实际用的很少。
KVC(key-valuecoding、键-值编码)
按以前的做法,要改变对象的属性,要么直接对对象成员变量赋值,要么调用它的setter方法。
KVC: 是一种允许动态操作对象属性的机制。
KVC只有2个方法:
- [对象 setValue:值 forKey:属性名] —— 相当于调用 [对象 set属性名:值];
- [对象 valueForKey:属性名] —— 相当于 [对象 属性名]
用了KVC之后,forKey后面的属性名支持变量,因此可以动态变化,这就是优势。
上面这两个方法都来自于NSObject。
KVC的查找顺序
[user setValue: 值 forKey:@"name"];
查找顺序如下:
(1)KVC会查找user是否有setName:方法。如果有,就执行setName:方法。
(2)如果setName:不存在,KVC会查找user是否存在名为_name的成员变量,如果有,直接对_name赋值。
(3)如果setName:不存在,_name也不存在,KVC会查找user是否存在名为name的成员变量,如果有,直接对name赋值。
(4)如果上面3步都没有找到,就会激发setValue:forUndefinedKey:方法。
setValueForUndefinedKey:是系统定义的方法,默认行为就是报错,我们可以重写该方法。
[user valueForKey: @"name"];
查找顺序如下:
(1)KVC会查找user是否有name方法。如果有,就执行name方法。
(2)如果name不存在,KVC会查找user是否存在名为_name的成员变量,如果有,直接返回_name的值。
(3)如果name:不存在,name也不存在,KVC会查找user是否存在名为name的成员变量,如果有,直接返回name的值。
(4)如果上面3步都没有找到,就会激发valueForUndefinedKey:方法。
valueForUndefinedKey:是系统定义的方法,默认行为就是报错,我们可以重写该方法。
处理不存在的key
当你通过kvc对不存在的key进行设值时,系统自动激发setValue:forUndefinedKey:方法。
setValue: forUndefinedKey:方法默认的行为是报一堆的错误信息。
如果我们希望改变这种行为,可以重写该方法。
当你通过kvc访问不存在的key对应的值时,系统自动激发valueForUndefinedKey:方法。
valueForUndefinedKey:方法默认的行为是报一堆的错误信息。
如果我们希望改变这种行为,可以重写该方法。
处理nil值
对于KVC而言,如果属性是指针类型,可以为之设置nil值的。如果属性是基本类型,它是不能接受nil值的。
如果程序尝试对不能接受nil值的属性,使用kvc设置为nil,程序将会自动激发setNilValueForKey:方法。
setNilValueForKey:是系统定义的方法,默认行为就是报错,我们可以重写该方法。
重写setNilValueForKey:方法即可让程序获得对nil值进行任意定制处理的机会。
KVC另外2个高级方法:用于处理key路径。
-setValue: forKeyPath: key路径
-valueForKeyPath: key路径
这两个高级方法只是将原来的forKey:,改成了forKeyPath:,这样就可以为复合属性设置值。
KVO(key-value observing,键、值监听)
它可用于监听某个对象的状态发生了修改。
在cocoa touch典型开发中,model组件负责数据部分,但view负责显示部分,此时当model发生改变时,View需要立即更新自己。
通常来说,有如下几种做法:
1. 让model组件持有一个view组件的引用,当model的状态发生改变时,model即可调用view的方法来更新view的状态。
缺陷:由于View必须依赖model(因为它显示的数据来自model),这样就形成相互依赖,很糟糕的设计。
2. 利用iOS的通知中心来实现。model发生改变时,会发送一个消息给通知中心;而view组件收到消息之后,立即去获取model数据,更新自己。
缺陷:这么小的功能,没必要用iOS的通知中心。
相对来说,比较简单、方便、轻度的解决方案就是此处KVO。
——只要让View作为model的监听器即可,当model的状态发生改变时,View特定的监听方法就会被激发。
【注意:】这个监听方法也属于重写父类的方法。
(1) 注册监听器。(view监听模型)
被监听的对象 addObserver: 监听器forKeyPath: 被监听的属性 options: context:
(2) 重写监听器的特定方法。
observeValueForKeyPath: ofObject:change: context:
最后还要注意:应该重写监听器的dealloc方法,保证系统回收该对象时,取消监听器。
对象的初始化
创建对象要分为2部分:
1. 分配内存。 [类名 alloc]
2. 执行初始化。 init方法。
init方法,也被称为【构造器】
NSObject本身提供了init方法,因此程序也可以自行去实现init方法。
重写NSObject的init方法
NSObject的init方法只是将所有的成员变量初始化为广义的0.
如果你希望执行默认的初始化,就可以重写init方法。
便利的初始化方法—— 带参数的初始化方法。
initWithXxx: yyy: zzz: www: ...
很多时候,还会提供一个类方法,用于执行对象的初始化。该方法名通常为: 类名WithXxx:yyy: zzz: www: ...
继承
面向对象的3大特征之一。
什么是继承?继承是一种非常重要的代码复用的手段:子类继承了父类,子类就可直接获取父类的成员变量、方法、初始化方法。
继承关系,是一种由一般到特殊的关系。子类,其实就是一种特殊的父类。
面向对象的继承,其实是一种类与类之间关系,不是对象与对象之间的关系。
子类,继承父类;可认为子类(小类)是一种特殊的父类(大类)
继承语法
@interface 类名 : 父类
@end
Object-C是一门单继承的语言,因此每个类最多只能有一个直接父类。
@interface Fruit : NSObject
@end;
@interface Apple : Fruit
@end;
@interface RedFuji: Apple
@end
RedFuji的直接父类(immediateparent)是Apple,其他父类还有Fruit、NSObject。
一个结论:一些类都是NSObject的子类。一切皆对象!
重写父类的方法
有时候,子类需要重新定义自己的行为,此时就要考虑重写父类的方法。
重写父类方法:将父类方法的方法签名拷贝过来(也就要子类方法与父类方法的签名相同),但方法体是重新定义的。
【注意:】子类重写父类方法时,并不需要在子类的接口部分重新声明。
一旦重写了父类的方法之后,当程序执行子类中重写过的方法时,程序将会表现出被重写后的行为。
super关键字
总是用于显式调用父类的方法。