通过 指针->成员变量名 方式给成员变量赋值具有危险性, 可能被赋值为不合理的数值。不允许通过指针直接修改成员变量, 就要去掉@public。 通过方法来修改成员变量的值, 方法中可以加代码保证接收的值是合理的赋值。
设置成员变量属性值的方法, 通常称为该属性的set方法, 命名方式为 setAge: , 接收一个相同类型的参数,形参的名称不能和成员变量名相同。
举例:
- - (void)setAge:(int)newAge
- {
- // 对传进来的参数值进行过滤, 保证数据安全性
- if (newAge <= 0)
- newAge = 1;
- // 把参数值赋给成员变量
- age = newAge;
- }
在 main函数中调用set方法:
[stu setAge:18]; // 赋值为18
[stu setAge:-18]; // 默认赋值为1
以后就不要写@public了, 必须要通过set方法来设置成员变量。 可以保证成员变量数据的合理性。
二、 get方法
没有@public, 我们就不能通过 stu->age 来查看成员变量的值 (被保护)。我们要通过调用方法来返回成员变量值。
get方法: 返回该对象的成员变量值。
举例
- - (int) age
- {
- return age;
- }
在main函数中调用get方法获得age属性的值:
int myAge = [stu age];
注意: 不是所有的成员变量都有set和get方法, 如果有的成员变量是只读属性(read only), 只能被访问, 不能被修改, 这样的情况就可以只提供get方法, 不写set方法。
三、 封装的好处
封装可以保证数据的安全性。 如果要给成员变量赋值, 必须通过set方法进行访问, 在set方法中, 可以添加对不合理的属性值的过滤。
四、 命名规范
在get方法中, 方法名和返回的成员变量名是一样的, 可读性不好。 所以规范成员变量名以下划线开头, 便于和get方法名和局部变量名区分。
成员变量名规范以
- @interface Student : NSObject
- {
- int _age; // 规范的的成员变量名
- }
- @end
- - (void) setAge:(int)age
- {
- _age = age; // 这样set方法的形参名就可以和成员变量同名了(没有下划线)
- }
- - (int) age
- {
- return _age;
- }
- // 调用是相同的
- [stu setAge:18];
- int myAge = [stu age];
*** 继承 ***
一、 基本概念
类B继承了类A, 那么类B具有类A的所有属性和方法。
在OC中继承关系用 : 表示。
比如我们要写一个Dog类和一个Cat类, 两个类拥有相似的成员变量和方法, 不想把代码复制很多遍。可以写一个Animal类, 把猫和狗相同的内容放到Animal里, 然后让Dog和Cat继承Animal类。
二、 优缺点
继承的好处: 子类拥有父类的所有成员变量和方法, 减少重复代码. 建立类之间的关系。
继承的缺点: 代码耦合性太强 (类之间的关系太紧密, 如果某个类坏了, 另一个类会受影响)。
我们的类如果不继承NSObject就没有new方法, 就没有创建对象的能力。我们的类能够调用new方法, 说明NSObject类拥有new方法。可以查看NSObject类的说明, 找到 +new 方法。
NSObject是基类, 几乎所有的类最终都是继承于它。也有的类不是继承NSObject的, 比如NSProxy, 它也是一个基类。
子类有自己的成员变量方法和父类的所有成员变量和方法 (以及父类的父类的)。
*** 多态 ***
有继承才有多态。
多态就是可以用父类的指针指向子类的对象. 不管用什么指针, 调用方法时调用的都是对象的方法。
一、 多态的好处:
可以使用父类指针来代表各种子类对象调用函数,节省代码。
比如, 我们有一个Cat类, 有eat方法; 还有一个Dog类, 也有eat方法
- @interface Cat : Animal
- - (void) eat;
- @end
- @implementation Cat
- - (void) eat
- {
- NSLog(@"Cat is eating");
- }
- @end
我们想写函数来喂动物, 喂Dog和Cat由于参数类型不同, 需要对每种类型都写一个函数:
- void feed(Dog *d)
- {
- [d eat];
- }
- void feed2(Cat *c ) // 不允许同名函数
- {
- [c eat];
- }
- int main()
- {
- Dog *d = [Dog new];
- feed (d); // 喂狗
- Cat *c = [Cat new];
- feed2 (c); // 喂猫
- return 0;
- }
这两个函数体是很相似的, 而且如果有更多的动物, 还要写很多相似的函数, 很麻烦, 还要使用不同的名字。
这样,就可以使用多态来简化,写一个Animal类, 作为Dog 和Cat的父类, 这样在函数中就可以使用Animal *指针, 来指代Dog或Cat类型的参数:
- void feed(Animal *a) // a既可以是Dog, 也可以是Cat, 也可以是其它Animal
- {
- [a eat]; // 调用对象的eat方法
- }
- int main()
- {
- Dog *d = [Dog new];
- feed (d); // 调用的是Dog的eat方法
- Cat *c = [Cat new];
- feed (c); // 调用的是Cat的eat方法
- Animal *a = [Animal new];
- feed (a);
- }
这样只需要写一个函数, 很方便, 而且调用的还都是每个对象自身的方法。
二、 多态的局限性
不建议用父类指针调用子类的特有方法。
如果用父类指针调用子类在父类中没有的方法, 虽然调用的是子类对象的方法, 但是编译器会报警告, 因为它只能看到用父类的指针调用一个父类没有的方法。
举例
- @implementation Animal
- @end
- @implementation Dog
- - (void) run { ... }
- @end
- int main()
- {
- Animal *a = [Dog new];
- [a run];
- return 0;
- }
- 可以正确运行, 但是编译器会警告。
- 如果必须要这么写, 规范方法是进行强制类型转换:
- int main()
- {
- Animal *a = [Dog new];
- Dog *d = (Dog *)a;
- [d run];
- return 0;
- }