对象初始化
基本初始化
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
- (id) init {
if (self = [super init]) {
self.brand = @"福特";
self.model = @"focous";
self.color = @"白色";
}
return self;
}
带有2个初始化方法 引用的时候需要给第三个属性提前在方法里面赋值
- (id) initWithBrand:(NSString*) brand model: (NSString*) model {
if (self = [super init]) {
self.brand = brand;
self.model = model;
self.color = @"白色";
}
return self;
}
- (id) initWithBrand: (NSString*) brand model: (NSString*) model color :(NSString*) color {
if(self = [self initWithBrand :brand model :model]) {
self.color = color;
}
return self;
}
@end
解释
对于以上初始化方法
- 第一个是正常的不带有方法的初始化在内部已经给成员变量car1的属性赋值
- 第二个是带有2个方法的初始化
- 第三个是带有三个方法的初始化 就类似于正常封装的方法的引用
通过使用便利的初始化,程序可以在创建对象时即可初始化对象的属性,避免对象创建完成后还要通过调用对象的setter方法来初始化对象的属性值
继承
继承实例-重写父类的方法
当父类里面规定了一个方法的时候,我们如果需要重写这个方法就直接在子类的实现里面重写该方法即可
父类
#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(@"重写方法--我属于鸟类不能飞");
}
-(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 {
fkbase *bc = [[fkbase alloc] init];
[bc base];
[bc test];
fkSubclass *sc = [[fkSubclass alloc] init];
[sc base];
[sc test];
fkbase *ct = [[fkSubclass alloc] init];
[ct base];
[ct test];
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实例方法了
|