文章目录
对象初始化
基本初始化
NSObject 提供的 init 方法虽然可以完成初始化,但由于它只是完成最基本的初始化,因此,对象的所有成员变量依然为0。
在实际编程过程中,可以提供自己的 init 方法,这个 init 方法实际上就是重写 NsObiect的 init 方法。当重写 init 方法时,开发者可以加入任意的自定义处理代码对属性执行初始化。
便利初始化
- 在以下的演示中 第一个方法是重写了init方法,这个方法对下面的初始化总是固定的不能根据参数动态的处理执行初始化,实际上 我们可以自己写一些OC初始化,这些初始化建议init开头,可以带一些参数;
3种便利初始化
接口部分
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface fkuser : NSObject
@property (nonatomic, copy) NSString* brand;
@property (nonatomic, copy) NSString* model;
@property (nonatomic, copy) NSString* color;
// 声明两种新的初始化方法
- (id) initWithBrand: (NSString*) brand model: (NSString*) model;
- (id) initWithBrand: (NSString*) brand model: (NSString*) model color :(NSString*) color;
@end
NS_ASSUME_NONNULL_END
- 上面接口部分定义了2个字定义的initxxx方法,这两个方法可以根据参数执行自定义初始化
- -上述接口没有对init方法进行声明因为init是一个不带参数的初始化
实现部分
这里演示了三个初始化方法
#import "fkuser.h"
@implementation fkuser
//@synthesize brand = _brand;
//@synthesize model = _model;
//@synthesize color = _color;
// 3种便利化的初始化方法
// 1. 重新写init方法
- (id) init {
if (self = [super init]) {
// 为对象的属性赋值
// 之前学到的点方法赋值
self.brand = @"福特";
self.model = @"focous";
self.color = @"白色";
}
return self;
}
//2. 设置
带有2个初始化方法 引用的时候需要给第三个属性提前在方法里面赋值
- (id) initWithBrand:(NSString*) brand model: (NSString*) model {
// 调用父类的inti方法执行初始化,将初始化得到的对象赋值给self对象
// 如果self不为nil,表明父类init方法初始化成功
if (self = [super init]) {
self.brand = brand;
self.model = model;
self.color = @"白色";
}
return self;
}
// 3.带有3个初始化方法 引用的时侯类似于正常封装方法的引用
- (id) initWithBrand: (NSString*) brand model: (NSString*) model color :(NSString*) color {
// 调用父类的initWithBrand:(NSString*) brand model: (NSString*) model方法执行初始化,将初始化得到的对象赋值给self对象
// 如果self不为nil,表明父类方法initWithBrand:brand初始化成功
if(self = [self initWithBrand :brand model :model]) {
self.color = color;
}
return self;
}
@end
解释
对于以上初始化方法
- 第一个是正常的不带有方法的初始化在内部已经给成员变量car1的属性赋值
- 第二个是带有2个方法的初始化
- 第三个是带有三个方法的初始化 就类似于正常封装的方法的引用
通过使用便利的初始化,程序可以在创建对象时即可初始化对象的属性,避免对象创建完成后还要通过调用对象的setter方法来初始化对象的属性值
继承
- 继承是面向对象的三大特征之一,更是实现软件复用的手段
- 特点:在OC语言中每个子类只有一个直接父类
- 通过“父类”语法来实现,实现继承的类被称为子类,被继承的类被称为父类,也称其为基类、超类。父类和子类的关系是一种“一般和特殊”的关系。例如
水果和苹果的关系,苹果继承了水果,苹果是水果的子类,苹果是一种特殊的水果。
子类可继承父类的全部成员变量、全部方法(包括初始化方法)。 - OC中一个子类可以有一个直接父类 和 无限多个间接父类
-
@interface FKfruit :FKPlant {....} @end @interface FKapple :FKfruit {....} @end
- 在上面的定义中 fruit是 apple的父类,plant是fruit的父类
- 所以plant是fruit的简介父类
继承实例-重写父类的方法
当父类里面规定了一个方法的时候,我们如果需要重写这个方法就直接在子类的实现里面重写该方法即可
父类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface fkbird : NSObject
-(void) fly;
@end
NS_ASSUME_NONNULL_END
#import "fkbird.h"
@implementation fkbird
-(void) fly {
NSLog(@"我能飞");
}
@end
子类
#import <Foundation/Foundation.h>
#import "fkuser.h"
#import "fkbird.h"
@interface fkbird1 : fkbird
@end
@implementation fkbird1
//重写方法是在类的实现部分直接重写方法
-(void) fly {
NSLog(@"重写方法--我属于鸟类不能飞");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
fkbird1 *bird = [[fkbird1 alloc] init];
[bird fly];
}
return 0;
}
这种子类包含与父类同名方法的现象被称为方法重写。也被称为方法覆盖 (Override)。可以说,子类重写了父类的方法,也可以说子类覆盖了父类的方法。
方法的重写必须注意方法签名关键字要完全相同,也就是方法名和方法签名中的形参标签都需要完全相同,否则就不能算方法重写
super 关键字
如果需要在子类方法中调用飞类被覆盖的方法,使用super关键字来调用父类被覆盖的方法,为上面的bird1的类添加一个方法,在这个方法调用bird被覆盖的fly方法
调用被覆盖的方法
@implementation fkbird1
//重写方法是在类的实现部分直接重写方法
-(void) fly {
NSLog(@"重写方法--我属于鸟类不能飞");
}
//利用super关键字
-(void) oldfly {
[super fly];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
fkbird1 *bird = [[fkbird1 alloc] init];
[bird fly];
[bird oldfly];
}
return 0;
}
效果
2022-05-20 10:31:38.056989+0800 OC对象初始化和=AND继承与多态[3161:327275] 重写方法--我属于鸟类不能飞
2022-05-20 10:31:38.057181+0800 OC对象初始化和=AND继承与多态[3161:327275] 我能飞
Program ended with exit code: 0
关于super特点
- super 用于限定该对象调用他从父类继承得到的属性或者方法
- super即可出现在类方法里也可以出现在实例方法里,在类方法里使用super调用父类的方法时,被调用的父类方法只能是类方法,在实例方法中super调用父类方法被调用的反之只能是实例方法(在哪调用就用哪的)
super的其他
当子类继承父类的时候子类可以获得服父类中定义的成员变量,因此子=子了接口不允许定义与父类接口重名的成员变量
- 但父类实现部分的定义成员变量时候,子类在接口和实现定义的成员变量都可以与父类实现部分定义的成员变量同名
- 父类接口部分定义了_a的成员变量字类的实现部分依旧可以定义为_a的成员变量
- 当子类的实现部分定义了与父重名的成员变量,子类的成员变量就会隐藏父类的成员变量因此此时可通股票调用父类的方法来访问父类的被隐藏的成员变量;
在这里插入图片描述
多态
为何出现多态
Objentive-C 指针类型的变量有两个:一个是编译时类型,
一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象洪定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态 (Polymorphism)
代码实例多态
父类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface fkbase : NSObject
-(void) base;
-(void) test;
@end
NS_ASSUME_NONNULL_END
#import "fkbase.h"
@implementation fkbase
- (void) base {
NSLog(@"父类的base方法");
}
- (void) test {
NSLog(@"父类即将被字类覆盖的方法");
}
@end
子类
#import <Foundation/Foundation.h>
#import "fkbase.h"
@interface fkSubclass : fkbase
- (void) sub;
@end
@implementation fkSubclass
-(void) test {
NSLog(@"子类覆盖父类的方法");
}
-(void) sub {
NSLog(@"子类自己用来测试多态的方法");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
//此时的bc类所用方法和父类fkbase是一样的 不会出现多态
fkbase *bc = [[fkbase alloc] init];
[bc base];
[bc test];
//sc也和上面的bc一样不会出现多态
fkSubclass *sc = [[fkSubclass alloc] init];
[sc base];
[sc test];
//此时出现了多态
fkbase *ct = [[fkSubclass alloc] init];
[ct base];
[ct test];
// [ct sub];
//此时运行会发生错误,因为我们编译类型是fabase但是运行类型是fksubclass
//解决
id dyna = ct;
[dyna sub];
}
return 0;
}
第一次错误
上面main0函数中注释掉了[ct sub];,这行代码会在编译时引发错误。虽然
ct 变量实际指向的对象确实包含sub 方法(例如,可以通过 ct来执
行该方法),但因为它的编译时类型为 FKBase, 因此编译时无法调用sub方法。
No visible @interface for 'fkbase' declares the selector 'sub'
id转化类型之后成功编译
2022-05-20 11:35:29.602767+0800 OC对象初始化和=AND继承与多态[3378:357254] 父类的base方法
2022-05-20 11:35:29.602988+0800 OC对象初始化和=AND继承与多态[3378:357254] 父类即将被字类覆盖的方法
2022-05-20 11:35:29.603019+0800 OC对象初始化和=AND继承与多态[3378:357254] 父类的base方法
2022-05-20 11:35:29.603034+0800 OC对象初始化和=AND继承与多态[3378:357254] 子类覆盖父类的方法
2022-05-20 11:35:29.603048+0800 OC对象初始化和=AND继承与多态[3378:357254] 父类的base方法
2022-05-20 11:35:29.603060+0800 OC对象初始化和=AND继承与多态[3378:357254] 子类覆盖父类的方法
2022-05-20 11:35:29.603071+0800 OC对象初始化和=AND继承与多态[3378:357254] 子类自己用来测试多态的方法
Program ended with exit code: 0
多态的解释
注意:
指针变量在编译阶段只能调用其编译时(前面的)fkbase 类型所具有的方法,但运行时则执行
其运行时(后面的)fksubclass类型所具有的方法。因此,编写 Obiective-C 代码时,指针变量只能调用声明该变量时所用类中包含的方法。例如,通过 NSObject*
p = [[FKPerson alloc] init]
代码定义一个变量p,则这个p只能调用 NSObject 类的方法,而不能FKPerson 类里定义的方法;
- 为了解决编译时类型检查的问题,Objective-C 提供了一个 id 类型,id 类型的变量可被赋值为任意类型的对象或任意类型的指针变量,而且使用 id 类型的变量可以调用该变量实际所指对象的方法。比如上面程序的最后一行粗体字代码,程序将ct 类型的指针变量赋值给id类型的变量 这样程序就可以调用fksubclass里的sub实例方法了