美文网首页ios
day23 self 与 super , 父类调用子类方法 -

day23 self 与 super , 父类调用子类方法 -

作者: wwwying9 | 来源:发表于2016-04-01 22:54 被阅读3275次

    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一个月,以上内容都是瞎写,被误导了不要打我.
    其实我也想知道真相

    相关文章

      网友评论

        本文标题:day23 self 与 super , 父类调用子类方法 -

        本文链接:https://www.haomeiwen.com/subject/epmylttx.html