day23
1.self 與 super , 父類調(diào)用子類方法
1.1 self 與 super
前面也寫(xiě)到了self 與 super 的區(qū)別,在 OC Examination 那天中.
內(nèi)容如下:
不管是 self 還是 super 其消息主體依然是 self ,也就是說(shuō) self 和 super 指向的
是同一個(gè)對(duì)象迫卢。只是 查找方法的位置 區(qū)別串述,一個(gè)從本類,一個(gè)從本類的超類够挂。
其實(shí)這段話是從網(wǎng)上找的,其含義并不是太明白.
- 為什么self和super指向的依舊是self本身?
記得好像其它語(yǔ)言中,self指向的就是本類,而super指向的就是父類.
- 為什么self和super要這樣去定義?
1.2 initWithFrame: 方法
今天學(xué)習(xí)的是關(guān)于自定義控件, 自定義控件時(shí)一般都需要重寫(xiě)構(gòu)造方法來(lái)初始化該控件中的子控件,這時(shí)候最先想到的就是重寫(xiě)init方法,在init中創(chuàng)建子控件,初始化等操作.這樣創(chuàng)建控件的時(shí)候,調(diào)用init方法就好了.這樣確實(shí)可以做到.但是有人在創(chuàng)建控件時(shí)還想把控件里需要的參數(shù)傳遞進(jìn)去,于是再定義一個(gè)" - (instancetype)initWithShop:(XMGShop *)shop " 方法. 或者定義一個(gè)類工廠方法 "+ (instancetype)shopViewWithShop:(XMGShop *)shop". 一運(yùn)行,結(jié)果什么控件也看不到.因?yàn)樯鲜龅膬蓚€(gè)方法不會(huì)來(lái)到init方法,不會(huì)去創(chuàng)建子控件,所以你什么也看不到.
這時(shí)候有人會(huì)告訴你" 添加子控件,做初始化的設(shè)置,init方法內(nèi)部會(huì)調(diào)用initWithFrame, 所以你只需要重寫(xiě)initWithFrame方法就好了", 于是把init方法改寫(xiě)成了initWithFrame.以運(yùn)行,結(jié)果正確.
但是為什么重寫(xiě)init方法不行,重寫(xiě)initWithFrame方法就可以?
因?yàn)?init方法內(nèi)部會(huì)調(diào)用initWithFrame ,更準(zhǔn)確的說(shuō)法是:父類中的init方法會(huì)調(diào)用init方法內(nèi)部會(huì)調(diào)用initWithFrame方法.
這回又有點(diǎn)不明白了,方法是一層一層往上(父類)調(diào)用(本類中找不到,往父類中找,父類中找不到,往爺爺類中找...),你去調(diào)用父類的init方法,怎么就最終掉到了本類中的initWithFrame方法了?
有人說(shuō)這是因?yàn)镺C中的動(dòng)態(tài)特性,就是說(shuō)"在運(yùn)行時(shí)才去判斷對(duì)象的真實(shí)類型". 也就是說(shuō),你去調(diào)用父類的init方法,結(jié)果在父類中發(fā)現(xiàn)這個(gè)對(duì)象是子類(本類)的對(duì)象,于是找方法就跑到了子類的方法列表中去找了,于是就調(diào)用了子類(本類)的的方法了.
自定義控件的部分代碼:
@interface XMGShopView ()
@property (nonatomic ,weak)UIImageView *iconImageView;
@property (nonatomic ,weak)UILabel *namelabel;
@end
@implementation XMGShopView
/*
//重寫(xiě)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改寫(xiě)成initWithFrame
// 添加子控件,做初始化的設(shè)置,init方法內(nèi)部會(huì)調(diào)用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];
}
/**
* 布局子控件,設(shè)置子控件的frame
*/
- (void)layoutSubviews
{
// 這里一定要調(diào)用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);
}
/**
* 設(shè)置數(shù)據(jù)
*/
- (void)setShop:(XMGShop *)shop
{
_shop = shop;
self.iconImageView.image = [UIImage imageNamed:shop.icon];
self.namelabel.text = shop.name;
}
1.3 父類調(diào)用子類方法
但這跟self 和 super 有什么關(guān)系?
要想不論用什么方法創(chuàng)建對(duì)象,都會(huì)掉用本類的initWithFrame方法,這樣是怎么實(shí)現(xiàn)的呢?
想知道實(shí)現(xiàn)?可惜看不到源碼,要是能看到源碼還會(huì)有這么一大推的廢話么.
去實(shí)現(xiàn)這個(gè)功能就可能跟 self 和 super 有關(guān)系了.
下面是模擬實(shí)現(xiàn)的代碼,代碼中定義了一個(gè)Person ,一個(gè)GoodPerson,繼承與Person. (把initWithName當(dāng)做initWithFrame)
并且不論用什么方法創(chuàng)建GoodPerson對(duì)象,都將調(diào)用GoodPerson 類中的 initWithName 方法, 就像自定義控件中,不論你用什么方法創(chuàng)建控件,都將調(diào)用本類中的 initWithName 方法.
其實(shí)這是因?yàn)樵诟割?person)中調(diào)用了[self initWithName:nil], 而此時(shí)self的值打印出來(lái)是子類的類型(GoodPerson),所以才有從父類的方法調(diào)用子類的方法這樣的跳轉(zhuǎn).
所以才有這樣的兩句話:
- 不管是 self 還是 super 其消息主體依然是 self ,也就是說(shuō) self 和 super 指向的 是同一個(gè)對(duì)象。只是 查找方法的位置 區(qū)別,一個(gè)從本類瓢省,一個(gè)從本類的超類。
- 添加子控件,做初始化的設(shè)置寫(xiě)在initWithFrame中,init方法內(nèi)部會(huì)調(diào)用initWithFrame, 所以你只需要重寫(xiě)initWithFrame方法,不管使用什么方法創(chuàng)建控件,都一定會(huì)調(diào)用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;
}
/*
輸出結(jié)果:
Person --- initWithName
GoodPerson --- initWithName
Person --- init
<GoodPerson: 0x100603a20>
GoodPerson --- initWithAge
age = 10
*/
/*
調(diào)用關(guān)系:
GoodPersonWithAge -> initWithAge (GoodPerson) ->
init(Person) -> initWithName(GoodPerson) ->
initWithName(Person) -> (NSObj 的init方法) -> 開(kāi)始返回.
*/
我也是剛接觸OC一個(gè)月,以上內(nèi)容都是瞎寫(xiě),被誤導(dǎo)了不要打我.
其實(shí)我也想知道真相