一、封装
1. 面向对象的三大特性:封装(成员变量)、继承和多态
在OC语言中,使用@interface和@implementation来处理类。(图片来源于官网)
@interface就好像暴露在外面的时钟表面,像外界提供展示以及接口。@implementation就好像隐藏在时钟内部的构造实现,把具体的实现封装了起来。
2. 封装:隐藏内部实现,稳定外部接口.
封装就是定义类,定义属性,定义方法;
3. Set方法
在开发过程中,考虑到安全性要求,我们一般不在成员变量名前面使用@public、@protected等关键字修饰,而是使用Set方法来为对象提供成员变量的值。在set方法的内部也可以对一些不合理的赋值进行筛选过滤。
Set方法的作用:为外界提供一个设置成员变量值的方法
命名规范:
1)方法名必须以set开头;
2)Set后面跟上成员变量的名称,首字母大写;
3)返回值一定是void;
4)一定要接收一个参数,而且参数类型需要和成员变量的类型一致;
5)形参名不能和成员变量名一样(苹果官方推荐成员变量名前加_以示区分);
Set方法的好处:(提供set 以及get 给别人使用就行)
1)不让数据暴露在外,保证了数据的安全性;
2)对设置的数据进行过滤 ;
#import <Foundation/Foundation.h>4. Get方法
// 声明
@interface Person : NSObject {
//
int _age;
}
//
- (void)setAge:(int)age;
- (void)print;
@end
// 实现
@implementation Person
//
- (void)setAge:(int)age {
_age = age;
}
- (void)print {
NSLog(@"age = %i", _age);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [[Person new] init];
[p setAge:80];
[p print];
}
return 0;
}
Get方法的作用:为调用者返回对象内部的成员变量
命名规范:
1)一定有返回值,返回值的类型和成员变量的类型一致
2)方法名和成员变量名一样
3)不需要接收任何参数
#import <Foundation/Foundation.h>注意1: 在实际的开发中,不一定 set 和 get 方法都会提供,如果内部的成员变量比如学生的学号这样的数据只允许外界读取,但是不允许修改的情况,则通常只提供 get 方法而不提供 set 方法。
// 声明
@interface Person : NSObject {
//
int _age;
}
//
- (void)setAge:(int)age;
- (void)print;
// get方法
- (int)age;
@end
// 实现
@implementation Person
//
- (void)setAge:(int)age {
_age = age;
}
- (void)print {
NSLog(@"age = %i", _age);
}
// get
- (int)age {
return _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [[Person new] init];
[p setAge:80];
[p print];
//
NSLog(@"利用get方法返回的值是: %i", [p age]);
}
return 0;
}
注意2: 成员变量名的命名以下划线开头,get方法名不需要带下划线,使用下划线开头有两个好处:(1)与get方法的方法名区分开来;(2)可以和一些其他的局部变量区分开来,下划线开头的变量,通常都是类的成员变量。
5. Self关键字
Self是一个指针,谁调用了当前方法,self就指向谁
【出现在对象方法中,就代表着当前对象,出现在类方法中,就代表着当前类】
Self的用途:
(1) 可以利用self->成员变量名访问当前对象内部的成员变量(仅在对象方法中)
(2) [self 方法名];可以调用其他的对象方法或者是类方法
关于self的关键字使用:http://blog.csdn.net/haojie2014/article/details/46908955
6.补充:OC弱语法
注意的是:Oc语言是一门弱语法的语言,编译的时候不会报错,所以这就要求我们开发过程中一定要按照既定的规范进行写代码。
1. oc在运行过程中才会检测对象有没有实现该相应的方法(动态监测),而即使没有写方法的实现代码,在编译、连接过程中只会显示警告,运行时会崩溃,如果在手机上运行运行遮掩的代码,运行过程中很可能就会造成闪退。
经典错误:不能识别消息发送给对象(运行时会监测方法有没有实现),会使程序运行时自动崩溃(闪退)
-[Person test]: unrecognized selector sent to instance 0x7f9738403420
2. 如果对象调用的方法只有声明没有实现,编译能通过(有警告:warning: method definition for ‘test‘ not found [-Wincomplete-implementation]),但是能连接成功,运行时还是会导致程序崩溃
3. 如果对象调用的方法只有实现,没有声明,编译能通过,能连接成功,也能顺利运行,但是不采用这样的写法
4. 只有类的声明没有类的实现也可以顺利运行,但是不采用这样的写法
#import <Foundation/Foundation.h>结果:
@interface Person : NSObject {
int _age;
}
- (void)test;
@end
@implementation Person
// 没有实现test方法
@end
int main(int argc, const char *argv[]) {
@autoreleasepool {
Person *p = [Person new];
[p test];
}
return 0;
}
二、 继承
1. 程序的世界与人类的“对象”世界在思想上没有什么区别,子类继承了父类,其实就拥有了父类的方法和属性(成员变量),继承及时代码优化公共部分交给父类。
子类可以直接复用父类中的成员。子类继承父类所有方法的声明和实现,非私有的实例变量以及协议,继承是要在 .h 文件中声明一下。继承具有单根性和传递性。
拓展:
Objective-C不支持多继承,这与c#, java一致,但与c++有点区别。也就是说每个类(除了根类NSObjec)外有且只能有一个基类,但是可以有另个或者若干个子类,这种关系正是树的结构特征,所以oc类呈现出的是树形层次的结构。
2. 继承的好处:
1). 抽取出了重复的代码;
2). 建立了类和类之间的联系;
缺点:
耦合性太强;
3. OC中的继承
@interface Animal:NSObject
//动物里继承了NSObject,获得NSObject类的方法;
@end
@interface Dog :Animal
//dog类继承Animal类
@end
注意:OC语言是单继承语言。在oc语言中,基本上所有类的根类都是NSObject类。
4. 继承的使用注意
1). 编译器从上往下执行,所以在子类前面至少应该要有父类的声明;
2). oc中不允许子类和父类拥有相同名称的成员变量名;
3). oc中的子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部寻找,如果没有则一层一层往上找;
提示:重写就是子类重新实现了父类中的某个方法,覆盖了父类以前的实现。
示意图:
其实就是创建一个Student *s = [[Student alloc] init];
此时会把Student类和这个类的父类加载紧内存;
提示:每个类中都有一个super class指针,该指针指向自己的父类。对象中有一个isa指针,该指针指向调用该对象的类。
5. 继承与组合
继承的适用场合:
1)当两个类拥有相同的属性和方法时,就可以将相同的属性和方法抽取到一个父类中。
2)当A类完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类(考虑),在这种情况下,也可以考虑使用组合。
继承: A 是 B;
组合:A 中有 B; 就是让B这个类作为类A的属性;
6. 关键字Super
Super关键字,在子类中重写方法时,可以让调用者跳过这一层而调用父类中的方法。
-(void)dealloc {作用:
//
[super dealloc];
}
1)直接调用父类的某一个方法;
2)Super处在对象方法中,那么就会调用父类的对象方法;super处于类方法中,那么就会调用父类的类方法;
使用场景: 子类重写父类方法时,想保留父类的一些行为
三、 多态
1. 多态在代码中的体现,就是多种形态,必须要有继承,没有继承就没有多态;
2. 在使用多态时,会进行动态监测,以调用真实的对象方法;
3. 多态在代码中的体现即父类指针指向子类对象;
4. 不同对象对同一消息的不同相应,子类可以重写父类的方法,多态就是允许方法重名 参数或返回值可以使父类行传入或返回。
#pragma mark - polymorphism5. 多态的局限性:父类类型的指针变量不能直接调用子类特有的方法
void polymorphism() {
// Animal
Animal *a = [[Animal alloc] init];
[a eat];
// Dog
Dog *d = [[Dog alloc] init];
[d eat];
// Guess what output
// The parent class pointer to the child object
Animal *a1 = [[Dog alloc] init];
NSLog(@"这里使用Dog初始化Animal!");
[a1 eat];
// 动态监测,调用方法时候会检测对象的真是类型
// Guess what output again
// Dog *d1 = [[Animal alloc] init];
// NSLog(@"这里使用Animal初始化Dog");
// [d1 eat];
}
Animal *a=[[Dog alloc] init];[a run]; //在Animal类中没有run方法,这里调用了狗对象的方法。解决方法:可以将a强制转换为Dog*类型的变量,如下:Dog *d=(Dog *)a;//使用强制转换,这里a和d指向的是同一个狗对象6. 总结
1)没有继承就没有多态;
2)代码的体现,父类类型的指针指向子类对象;
3)好处:如果函数方法参数中使用的是父类类型,则可以传入父类和子类对象,而不用再去定义多个函数来和响应的类进行匹配;
4)局限性:父类类型的变量不能直接访问子类特有的方法,如果一定要调用,则必须强制转化为子类特有的方法;
关于面向对象的三特性还可以阅读文章:http://blog.csdn.net/haojie2014/article/details/48064563(了解面向对象三大特性)