day23
1.self 与 super , 父类调用子类方法
1.1 self 与 super
前面也写到了self 与 super 的区别,在 OC Examination 那天中.
内容如下:
不管是 self 还是 super 其消息主体依然是 self ,也就是说 self 和 super 指向的
是同一个对象。只是 查找方法的位置 区别,一个从本类,一个从本类的超类。
其实这段话是从网上找的,其含义并不是太明白.
- 为什么self和super指向的依旧是self本身?
记得好像其它语言中,self指向的就是本类,而super指向的就是父类.
- 为什么self和super要这样去定义?
1.2 initWithFrame: 方法
今天学习的是关于自定义控件, 自定义控件时一般都需要重写构造方法来初始化该控件中的子控件,这时候最先想到的就是重写init方法,在init中创建子控件,初始化等操作.这样创建控件的时候,调用init方法就好了.这样确实可以做到.但是有人在创建控件时还想把控件里需要的参数传递进去,于是再定义一个" - (instancetype)initWithShop:(XMGShop *)shop " 方法. 或者定义一个类工厂方法 "+ (instancetype)shopViewWithShop:(XMGShop *)shop". 一运行,结果什么控件也看不到.因为上述的两个方法不会来到init方法,不会去创建子控件,所以你什么也看不到.
这时候有人会告诉你" 添加子控件,做初始化的设置,init方法内部会调用initWithFrame, 所以你只需要重写initWithFrame方法就好了", 于是把init方法改写成了initWithFrame.以运行,结果正确.
但是为什么重写init方法不行,重写initWithFrame方法就可以?
因为,init方法内部会调用initWithFrame ,更准确的说法是:父类中的init方法会调用init方法内部会调用initWithFrame方法.
这回又有点不明白了,方法是一层一层往上(父类)调用(本类中找不到,往父类中找,父类中找不到,往爷爷类中找...),你去调用父类的init方法,怎么就最终掉到了本类中的initWithFrame方法了?
有人说这是因为OC中的动态特性,就是说"在运行时才去判断对象的真实类型". 也就是说,你去调用父类的init方法,结果在父类中发现这个对象是子类(本类)的对象,于是找方法就跑到了子类的方法列表中去找了,于是就调用了子类(本类)的的方法了.
自定义控件的部分代码:
@interface XMGShopView ()
@property (nonatomic ,weak)UIImageView *iconImageView;
@property (nonatomic ,weak)UILabel *namelabel;
@end
@implementation XMGShopView
/*
//重写init方法
- (instancetype)init
{
if (self = [super init]) {
// 添加图片
UIImageView *iconImageView = [[UIImageView alloc] init];
[self addSubview:iconImageView];
self.iconImageView = iconImageView;
// 添加文字
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:nameLabel];
self.namelabel = nameLabel;
}
return self;
}
*/
//将init改写成initWithFrame
// 添加子控件,做初始化的设置,init方法内部会调用initWithFrame
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 添加图片
UIImageView *iconImageView = [[UIImageView alloc] init];
[self addSubview:iconImageView];
self.iconImageView = iconImageView;
// 添加文字
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:nameLabel];
self.namelabel = nameLabel;
}
return self;
}
- (instancetype)initWithShop:(XMGShop *)shop {
if (self = [super init]) {
self.shop = shop;
}
return self;
}
+ (instancetype)shopViewWithShop:(XMGShop *)shop {
return [[self alloc] initWithShop:shop];
}
/**
* 布局子控件,设置子控件的frame
*/
- (void)layoutSubviews
{
// 这里一定要调用super
[super layoutSubviews];
CGFloat shopW = self.frame.size.width;
CGFloat shopH = self.frame.size.height;
self.iconImageView.frame = CGRectMake(0, 0, shopW, shopW);
self.namelabel.frame = CGRectMake(0, shopW, shopW, shopH - shopW);
}
/**
* 设置数据
*/
- (void)setShop:(XMGShop *)shop
{
_shop = shop;
self.iconImageView.image = [UIImage imageNamed:shop.icon];
self.namelabel.text = shop.name;
}
1.3 父类调用子类方法
但这跟self 和 super 有什么关系?
要想不论用什么方法创建对象,都会掉用本类的initWithFrame方法,这样是怎么实现的呢?
想知道实现?可惜看不到源码,要是能看到源码还会有这么一大推的废话么.
去实现这个功能就可能跟 self 和 super 有关系了.
下面是模拟实现的代码,代码中定义了一个Person ,一个GoodPerson,继承与Person. (把initWithName当做initWithFrame)
并且不论用什么方法创建GoodPerson对象,都将调用GoodPerson 类中的 initWithName 方法, 就像自定义控件中,不论你用什么方法创建控件,都将调用本类中的 initWithName 方法.
其实这是因为在父类(person)中调用了[self initWithName:nil], 而此时self的值打印出来是子类的类型(GoodPerson),所以才有从父类的方法调用子类的方法这样的跳转.
所以才有这样的两句话:
- 不管是 self 还是 super 其消息主体依然是 self ,也就是说 self 和 super 指向的 是同一个对象。只是 查找方法的位置 区别,一个从本类,一个从本类的超类。
- 添加子控件,做初始化的设置写在initWithFrame中,init方法内部会调用initWithFrame, 所以你只需要重写initWithFrame方法,不管使用什么方法创建控件,都一定会调用initWithFrame.
完整代码:
- Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(instancetype)init;
-(instancetype)initWithName:(NSString *)name;
@end
- Person.m
#import "Person.h"
@implementation Person
-(instancetype)init{
if (self = [self initWithName:nil]) {
//if (self = [super init]) {
NSLog(@"Person --- init");
NSLog(@"%@",self);
}
return self;
}
-(instancetype)initWithName:(NSString *)name{
if (self = [super init]) {
NSLog(@"Person --- initWithName");
// NSLog(@"%@",self);
}
return self;
}
@end
- GoodPerson.h
#import "Person.h"
@interface GoodPerson : Person
@property(nonatomic,assign)int age;
-(instancetype)init;
-(instancetype)initWithName:(NSString *)name;
-(instancetype)initWithAge:(int)age;
+(instancetype)GoodPersonWithAge:(int)age;
@end
- GoodPerson.m
#import "GoodPerson.h"
@implementation GoodPerson
-(instancetype)init{
if (self = [super init]) {
NSLog(@"GoodPerson --- init");
}
return self;
}
-(instancetype)initWithName:(NSString *)name{
if (self = [super initWithName:name]) {
NSLog(@"GoodPerson --- initWithName");
}
return self;
}
-(instancetype)initWithAge:(int)age{
if (self = [super init]) {
_age = age;
NSLog(@"GoodPerson --- initWithAge");
}
return self;
}
+(instancetype)GoodPersonWithAge:(int)age{
return [[GoodPerson alloc] initWithAge:age];
}
@end
- main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "GoodPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
GoodPerson *p = [GoodPerson GoodPersonWithAge:10];
NSLog(@"age = %i",p.age);
}
return 0;
}
/*
输出结果:
Person --- initWithName
GoodPerson --- initWithName
Person --- init
<GoodPerson: 0x100603a20>
GoodPerson --- initWithAge
age = 10
*/
/*
调用关系:
GoodPersonWithAge -> initWithAge (GoodPerson) ->
init(Person) -> initWithName(GoodPerson) ->
initWithName(Person) -> (NSObj 的init方法) -> 开始返回.
*/
我也是刚接触OC一个月,以上内容都是瞎写,被误导了不要打我.
其实我也想知道真相