黑马程序员—OC—三大特性

时间:2022-01-04 00:31:21

*** 封装 ***
一、 set方法

通过 指针->成员变量名 方式给成员变量赋值具有危险性可能被赋值为不合理的数值。不允许通过指针直接修改成员变量就要去掉@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];

 

注意: 不是所有的成员变量都有setget方法如果有的成员变量是只读属性(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然后让DogCat继承Animal类。


二、 优缺点

继承的好处: 子类拥有父类的所有成员变量和方法减少重复代码建立类之间的关系。

继承的缺点: 代码耦合性太强 (类之间的关系太紧密如果某个类坏了另一个类会受影响)。

 

我们的类如果不继承NSObject就没有new方法就没有创建对象的能力。我们的类能够调用new方法说明NSObject类拥有new方法。可以查看NSObject类的说明找到 +new 方法。

NSObject是基类几乎所有的类最终都是继承于它。也有的类不是继承NSObject比如NSProxy, 它也是一个基类。

 

子类有自己的成员变量方法和父类的所有成员变量和方法 (以及父类的父类的)。


*** 多态 ***

有继承才有多态。

多态就是可以用父类的指针指向子类的对象不管用什么指针调用方法时调用的都是对象的方法。


一、 多态的好处:

可以使用父类指针来代表各种子类对象调用函数,节省代码。

 

比如, 们有一个Cateat方法; 还有一个Dog也有eat方法。

@interface Cat : Animal
- (void) eat;
@end
 
@implementation Cat
- (void) eat 
{
        NSLog(@"Cat is eating");
}
@end


  

我们想写函数来喂动物, DogCat由于参数类型不同需要对每种类型都写一个函数:

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 *指针来指代DogCat类型的参数:

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;
}



------- android培训、ios培训、期待与您交流! ----------

详细请查看:www.itheima.com