在前面的学习中有一些零星的要点,总结在这里。
1、匿名对象
[NSObject new];
上面的代码定义了一个匿名的对象,也就是说,没有指针指向这个对象,这种对象只有在创建的时候能进行操作,之后则无法调用,一般不建议使用,只做简单的了解。
2、数据监听
我们举一个常见的统计分数的例子,我们要求创建一个分数对象,包含数学、语文、英语分数等属性,以及总分和平均分属性,总分和平均分显然是通过各门课程的分数直接求出来的,不应该另行设置,我们看看该如何实现:
@interface Score : NSObject
// 注意这里的nonatomic不能省略,因为我们打破了自动线程管理的规范
@property (nonatomic) int mathScore;
@property (nonatomic) int chineseScore;
@property (nonatomic) int englishScore;
@property (nonatomic) int totalScore;
@property (nonatomic) int averageScore;
@end
@implementation Score
// set方法重写(取代自动生成的set方法)
- (void)setMathScore:(int)mathScore{
_mathScore = mathScore;
_totalScore = _mathScore + _chineseScore + _englishScore; // 监听
_averageScore = _totalScore / 3; // 监听
}
- (void)setChineseScore:(int)chineseScore{
_chineseScore = chineseScore;
_totalScore = _mathScore + _chineseScore + _englishScore; // 监听
_averageScore = _totalScore / 3; // 监听
}
- (void)setEnglishScore:(int)englishScore{
_englishScore = englishScore;
_totalScore = _mathScore + _chineseScore + _englishScore; // 监听
_averageScore = _totalScore / 3; // 监听
}
@end
int main(){
Score *s = [Score new]; // 创建新分数对象
s.mathScore = 80; // 赋值(每赋值一次,总分和均分都会计算一次)
s.chineseScore = 90; // 赋值(每赋值一次,总分和均分都会计算一次)
s.englishScore = 95; // 赋值(每赋值一次,总分和均分都会计算一次)
NSLog(@"totalScore is %d", s.totalScore); // 总分直接输出
NSLog(@"averageScore is %d", s.averageScore); // 平均分直接输出
return 0;
}
这种在参数设置的过程中,添加的代码,实际上实现了监听参数变化的效果,类似这样的处理,我们称之为监听。
3、对象里的对象
我们知道,一个对象内部可以有多个属性,属性可不可以是对象呢?这当然也是可以的,注意,当属性为对象的时候,实际上这个属性是一个指向另一个对象的指针。(其实,前面用到的NSString类型的字符串就是一个对象),我们来看下面的例子:
我们知道,一个对象内部可以有多个属性,属性可不可以是对象呢?这当然也是可以的,注意,当属性为对象的时候,实际上这个属性是一个指向另一个对象的指针。(其实,前面用到的NSString类型的字符串就是一个对象),我们来看下面的例子:
// Dog 类
@interface Dog : NSObject
@property NSString *name;
@end
@implementation Dog
@end
// Person 类
@interface Person : NSObject
@property NSString *name;
@property Dog *dog; // 狗作为人的一个参数
@end
@implementation Person
@end
int main(){
Dog *d = [Dog new]; //创建狗对象
d.name = @"Wowo";
Person *p = [Person new]; // 创建人对象
p.name = @"xiaoming";
p.dog = d; // 人对象获取狗对象
// 输出(注意d的属性是通过p取得的)
NSLog(@"I am %@, I have a dog named %@!", p.name, p.dog.name);
return 0;
}
当两个类之间没有什么逻辑关系,但是又有相同的属性时,我们一般是采用组合的方式来设计,相同的部分,我们提取为一个类,然后在另一个类里,把这个类当成一个属性。这和上面的例子的实现基本一致。
// Score 类
@interface Score : NSObject
@property int chineseScore;
@property int englishScore;
@property int mathScore;
@end
@implementation Score
@end
// Student 类
@interface Student : NSObject
@property NSString *name;
@property int number;
@property Score *score; // 分数类作为学生的一个属性(组合)
@end
@implementation Student
@end
这样一来,学生对象就有了分数对象里面的属性。只不过操作的时候,要多一个点语法操作。
5、self和super
self在OC中是一个特殊的指针,用于表示当前的对象,而super是一个编译器特定,他指向当前类的父类。
// Animal 类(父类)
@interface Animal : NSObject
@property int age;
- (void)showDetail;
@end
@implementation Animal
- (void)showDetail{
NSLog(@"Age is %d", self.age); // 通过self操作属性
}
@end
// Dog 类(子类)
@interface Dog : Animal
@property NSString *name;
@end
@implementation Dog
- (void)showDetail{ // 重写父类中存在的方法
NSLog(@"Name is %@", self.name); // 通过self操作属性
[super showDetail]; // 通过super访问父类方法
}
@end
// 主函数
int main(){
Dog *d = [Dog new];
d.name = @"Wowo";
d.age = 3;
[d showDetail]; // 该show方法将会同时调用父类的show方法
return 0;
}
输出如下:
2015-02-13 13:19:05.447 Study[21944:303] Name is Wowo
2015-02-13 13:19:05.450 Study[21944:303] Age is 3
6、重写
在上面的例子中已经出现了方法的重写,这种在父类中存在的方法,如果我们不希望子类继承,可以在子类中重写方法,只要和父类中的方法名称参数都一样即可,注意,不需要重新声明方法。在某个继承链中,当每一级别的类里都有某个方法时,方法的调用是从当前对象开始往上追溯的,遇到的第一个为执行的方法。
7、多态
定义对象时,类型指定为父类及以上的情况,可实现对象的多态。我们通过一个简单的例子来理解这个概念:
定义一个人的类,有一个喂食的方法,可以喂狗和猫东西吃。在实际操作中,我们可以抽象出动物的概念,猫和狗都继承自动物,我们只要能给动物喂食,相应的也能给猫和狗喂食,猫和狗的类中也可以重写出个性化的处理,不会出现干扰。
我们来看看怎么实现:
// Animal 类
@interface Animal : NSObject
- (void)eat;
@end
@implementation Animal
- (void)eat{ // eat 方法
NSLog(@"Animal eat!");
}
@end
// Dog 类 --> Animal 类
@interface Dog : Animal
@end
@implementation Dog
- (void)eat{ // 重写
NSLog(@"Dog eats!");
}
@end
// Cat 类 --> Animal 类
@interface Cat : Animal
@end
@implementation Cat
- (void)eat{ // 重写
NSLog(@"Cat eats!");
}
@end
// Person 类
@interface Person : NSObject
- (void)feedAnimal:(Animal*)animal;
@end
@implementation Person
- (void)feedAnimal:(Animal*)animal{
NSLog(@"Feed a %@", animal); // 输出的是具体对象
[animal eat]; // 先在具体对象内找方法
}
@end
// 主函数
int main(){
Person *p = [Person new];
Cat *c = [Cat new];
Dog *d = [Dog new];
[p feedAnimal:c]; // 可以对多种对象进行操作
[p feedAnimal:d]; // 可以对多种对象进行操作
return 0;
}
通过上面的代码,我们可以发现,定义的方法可以接收多种对象,而且每个对象能够做出不一样的处理,来看看输出结果:
2015-02-13 18:05:11.226 Study[22236:303] Feed a <Cat: 0x1002026d0>
2015-02-13 18:05:11.229 Study[22236:303] Cat eats!
2015-02-13 18:05:11.230 Study[22236:303] Feed a <Dog: 0x100202850>
2015-02-13 18:05:11.231 Study[22236:303] Dog eats!