重学iOS——4.三大特性和一些小trick
简介
OC作为一门优秀的面向对象的开发语言,那么必不可少的特性就是:封装、继承、多态。面向对象的三大特性也是OC语法中的核心内容,对于程序开发而言,三大特性贯穿始终!掌握好OC的这三大特性,无论是对于程序开发,无论是对于代码结构的优化,亦或是后期其他语言的学习,都能让我们快速上手!基本概念我是熟知的,但是对于一些小细节,我觉得很有必要在这里深刻的记录一下。
三大特性基本概念
- 封装:封装性就是隐藏实现细节,仅对外公开接口。
封装的作用:
- 数据安全,保障类的对象变量只能够通过类内部分方法进行访问,而不能直接访问,例如setter和getter方法。
- 降低程序之间的耦合度,程序员不需要去关心具体程序功能块内部的具体实现,只通过接口与外部进行通信。
-
继承:继承描述的是子类与父类之间的关系,父类是子类共有属性的又一层抽象。
继承图解.png
- B类继承A类,那么B类将拥有A类的所有属性和方法,此时我们说A类是B类的父类,B类是A类的子类
- C类继承B类,那么C类将拥有B类中的所有属性和方法,包括B类从A类中继承过来的属性和方法,此时我们说B类是C类的父类,C类是B类的子类
- 注意点:
~ 基类的私有属性能被继承,不能在子类中访问。
~ OC中的继承是单继承:也就是说一个类只能一个父类,不能继承多个父类
~ 子类与父类的关系也称为isA(是一个)关系,我们说 子类isA父类,也就是子类是一个父类,比如狗类继承动物类,那么我们说狗isA动物,也就是狗是一个动物。在如汽车继承交通工具,那么们说汽车isA交工工具,也就是汽车是一个交通工具
~ 继承的合理性:引用《大话西游》里的一句话来描述继承的。“人是人他妈生的,妖是妖他妈生的!”- 继承中重写方法的调用顺序:
~ 如果子类中有和父类中同名的方法, 那么我们称之为方法重写。
~ 继承中的方法调用顺序, 如果自己有就调用自己的, 如果自己没有就调用父类的
~ 方法的调用顺序, 先自己再父类, 如果父类中没有再爷爷类, 如果爷爷类再没有就找爷爷的爸爸类 ,如果一直找到NSObject类都没有找到, 那么就会报错:reason: '-[Iphone signalWithNumber:]: unrecognized selector sent to instance 0x1003043c0'
- 多态:多态就是某一类事物的多种形态,在程序中表现为:父类指针指向子类对象
多态的条件:
~ 有继承关系。
~ 子类重写父类方法。
~ 父类指针指向子类对象
~ 表现:当父类指针指向不同的对象的时候,通过父类指针调用被重写的方法的时候,会执行该指针所指向的那个对象的方法
多态的有点
~ 多态的主要好处就是简化了编程接口。它允许在类和类之间重用一些习惯性的命名,而不用为每一个新的方法命名一个新名字。这样,编程接口就是一些抽象的行为的集合,从而和实现接口的类的区分开来。
~ 多态也使得代码可以分散在不同的对象中而不用试图在一个方法中考虑到所有可能的对象。 这样使得您的代码扩展性和复用性更好一些。当一个新的情景出现时,您无须对现有的代码进行改动,而只需要增加一个新的类和新的同名方法。
self关键字和super关键字
- self关键字的使用
#import "Iphone.h"
@implementation Iphone
/*
如果self在对象方法中, 那么self就代表调用当前对象方法的那个对象
如果self在类方法中, 那么self就代表调用当前类方法的那个类
总结:
我们只用关注self在哪一个方法中 , 如果在类方法那么就代表当前类, 如果在对象方法那么就代表"当前调用该方法的对象"
注意:
>self会自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错
>不能在对象方法或者类方法中利用self调用当前self所在的方法
使用场景:
可以用于在对象方法之间进行相互调用
可以用于在类方法之间进行相互调用
可以用于区分成员变量和局部变量同名的情况
*/
+ (void)carameWithFlahlightStatus:(FlahlightStatus)status
{
if (status == kFlahlightStatusOpen) {
// NSLog(@"self = %p", self);
[self openFlahlight]; // p
}else
{
[self closeFlahlight];
}
NSLog(@"拍照");
}
+ (void)openFlahlight
{
NSLog(@"打开闪光灯");
// NSLog(@"self = %p", self);
// 死循环
[self openFlahlight]; // p
}
+ (void)closeFlahlight
{
NSLog(@"关闭闪光灯");
}
- (void)setCpu:(int)cpu
{
self->cpu = cpu;
}
@end
- super关键字的使用
super关键字是使用在有继承关系的子类中,通过super可以访问父类的对象方法和父类的类方法,具体用法与self基本相似,具体代码如下:
Phone.h&Phone.m
#Phone.h
#import <Foundation/Foundation.h>
typedef enum
{
kFlahlightStatusOpen,
kFlahlightStatusClose
} FlahlightStatus;
// 被继承的这个类我们称之为父类/ 超类
@interface Phone : NSObject
/**
* 根据闪光灯的状态拍照
*
* @param status 闪光灯的状态, 开/关
*/
+ (void)carameWithFlahlightStatus:(FlahlightStatus)status;
- (void)carameWithFlahlightStatus:(FlahlightStatus)status;
/**
* 打开闪光灯
*/
+ (void)openFlahlight;
/**
* 关闭闪光灯
*/
+ (void)closeFlahlight;
@end
______________________________内容分隔__________________________________
#Phone.m
#import "Phone.h"
@implementation Phone
- (void)carameWithFlahlightStatus:(FlahlightStatus)status;
{
NSLog(@"- carameWithFlahlightStatus");
}
+ (void)carameWithFlahlightStatus:(FlahlightStatus)status
{
if (status == kFlahlightStatusOpen) {
[self openFlahlight];
}else
{
[self closeFlahlight];
}
NSLog(@"拍照");
}
+ (void)openFlahlight
{
NSLog(@"打开闪光灯");
}
+ (void)closeFlahlight
{
NSLog(@"关闭闪光灯");
}
@end
Iphone.h&Iphone.m
#Iphone.h
#import <Foundation/Foundation.h>
#import "Phone.h"
@interface Iphone : Phone
+ (void)carameWithFlahlightStatus:(FlahlightStatus)status;
- (void)test;
@end
______________________________内容分隔__________________________________
#import "Iphone.h"
@implementation Iphone
+ (void)carameWithFlahlightStatus:(FlahlightStatus)status
{
NSLog(@"聚焦");
NSLog(@"调光");
NSLog(@"人脸识别");
/*
// 由于以下代码和父类中的一模一样, 所以只需调用父类写好的代码即可
if (status == kFlahlightStatusOpen) {
[self openFlahlight];
}else
{
[self closeFlahlight];
}
NSLog(@"拍照");
*/
// [self carameWithFlahlightStatus:status];
// 只需要利用super给父类的方法发送一个消息, 那么系统就会自动调用父类的方法
// 如果以后想在子类中调用父类的方法可以使用super
// 如果想在给父类方法进行扩展的同时保留父类的方法, 那么可以使用super调用父类同名的方法
[super carameWithFlahlightStatus:status];
}
- (void)test
{
/*
super在类方法中, 一定会调用父类的类方法
super在对象方法中, 一定会调用父类的对象方法
可以利用super在任意方法中调用父类中的方法
*/
[super carameWithFlahlightStatus:kFlahlightStatusOpen];
}
@end
OC中成员变量的修饰符:修饰符主要有: @public、@protected、@private、@package
@public
- 可以在其它类中访问被public修饰的成员变量
- 也可以在本类中访问被public修饰的成员变量
- 可以在子类中访问父类中被public修饰的成员变量
@private
- 不可以在其它类中访问被private修饰的成员变量
- 可以在本类中访问被private修饰的成员变量
- 不可以在子类中访问父类中被private修饰的成员变量
@protected
- 不可以在其它类中访问被protected修饰的成员变量
- 可以在本类中访问被protected修饰的成员变量
- 可以在子类中访问父类中被protected修饰的成员变量
- 注意: 默认情况下所有的实例变量都是protected
@package
- 介于public和private之间的
- 如果是在其它包中访问那么就是private的
- 如果是在当前代码所在的包种访问就是public的
实例变量修饰符作用域: 从出现的位置开始, 一直到下一个修饰符出现
如果没有遇到下一个实例变量修饰符, 那么就会修饰后面所有的实例变量
多态代码实现的详细用法
父类Animal类
#Animal.h
#import <Foundation/Foundation.h>
@interface Animal : NSObject
{
int _age;
}
- (void)eat;
@end
#Animal.m
#import "Animal.h"
@implementation Animal
- (void)eat
{
NSLog(@"吃东西");
}
@end
子类Dog类
#Dog.h
#import <Foundation/Foundation.h>
#import "Animal.h"
@interface Dog : Animal
- (void)kanJia;
@end
#Dog.m
#import "Dog.h"
@implementation Dog
- (void)eat
{
NSLog(@"啃骨头");
}
- (void)kanJia
{
NSLog(@"看家, 旺旺叫");
}
@end
子类Pig类
#Pig.h
#import <Foundation/Foundation.h>
#import "Animal.h"
@interface Pig : Animal
@end
#Pig.m
#import "Pig.h"
@implementation Pig
- (void)eat
{
NSLog(@"大口吃");
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Cat.h"
#import "Animal.h"
#import "Person.h"
#import "Pig.h"
int main(int argc, const char * argv[]) {
/*
Dog *d = [Dog new];
[d eat];
Cat *c = [Cat new];
[c eat];
*/
/*
// 多态: 事物的多种表现形态
// 动态类型: 在编译的时候编译器只会检查当前类型对应的类中有没有需要调用的方法
// 在运行时,系统会自动判断a1的真实类型
Animal *a1 = [Dog new];
[a1 eat];
// 注意点: 在多态中, 如果想调用子类特有的方法必须强制类型转换为子类才能调用
// [a1 kanJia];
Dog *d1 = (Dog *)a1;
[d1 kanJia];
*/
Dog *d = [Dog new];
Cat *c = [Cat new];
Pig *p = [Pig new];
[Person food:d];
[Person food:c];
[Person food:p];
/*
什么是多态:
事物的多种表现形态
在程序中如何表现:
父类指针指向子类对象
优点:
提高了代码的扩展性
注意点:
如果父类指针指向子类对象, 如果需要调用子类特有的方法, 必须先强制类型转换为子类才能调用
*/
return 0;
}
Description方法
1.基本概念
- NSLog(@"%@", objectA);这会自动调用objectA的description方法来输出ObjectA的描述信息.
- description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址)
- description方法是基类NSObject 所带的方法,因为其默认实现是返回类名和对象的内存地址, 这样的话,使用NSLog输出OC对象,意义就不是很大,因为我们并不关心对象的内存地址,比较关心的是对象内部的一些成变量的值。因此,会经常重写description方法,覆盖description方法 的默认实现
2.Description重写的方法
/**对象方法:当使用NSLog输出该类的实例对象的时候调用*/
-(NSString *) description
{
return [NSString stringWithFormat:@"狗腿数:%d,狗眼数%d\n",_legNum,_eyeNum];
}
/**类方法:当使用NSLog输出该类的类对象的时候调用:[类名 class]*/
+(NSString *) description
{
return @"+开头的description方法";
}
3.Description陷阱
- 千万不要在description方法中同时使用%@和self,下面的写法是错误的
- (NSString *)description {
return [NSString stringWithFormat:@"%@", self];
}
-
同时使用了%@和self,代表要调用self的description方法,因此最终会导致程序陷入死循环,循环调用description方法
-
当[NSString stringWithFormat:@“%@”, self]; 使用它时,循环调用,导致系统会发生运行时错误。
-
当该方法使用NSLog(“%@”,self) 时候, 系统做了相关的优化,循坏调用3次后就会自动退出
欢迎关注我的个人微信公众号,免费送计算机各种最新视频资源!你想象不到的精彩!
0.jpg