• Designated Initializer


    一个类,可能有很多初始化函数,但是有主次之分,最主要的初始函数应该对类内应当需要初始化的变量进行初始化。这个最主要的初始函数即Designated Initializer(指定初始化器),可以理解为是类的默认初始函数。比如,UIView的Designated Initializer是initWithFrame:而不是init:

    原则1.类的正确初始化过程应当依次调用子类到父类的Designated Initializer。即使是用父类的Designated Initializer初始化一个子类对象,也需要遵从这个过程

    原则2.如果子类指定了新的Designated Initializer,那么该函数必须调用父类的Designated Initializer。并且需要重写父类的Designated Initializer,将其指向子类新的Designated Initializer

    NSObject的Designated Initializer为init;
    UIView的Designated Initializer为initWithFrame:;
    TestView继承于UIView,它有一个属性Name,所以它的Designated Initializer为initWithFrame:andName:

    假如不遵循第2点,initWithFrame:andName:的实现如下

    - (id)initWithFrame:(CGRect)frame andName:(NSString *)name
    {
        if (self = [super init])
        {
            self.name = name;
        }
        return self;
    }

    测试1:使用子类的Designated Initializer初始化子类对象

    TestView *testView = [[TestView alloc] initWithFrame:CGRectZero andName:@""];

    调用顺序如下:
    1)TestView的initWithFrame:andName:
    2)UIView的init
    3)UIView的initWithFrame
    4)NSObject的init
    此时没问题,3个类的Designated Initializer都被调用了

    测试2:使用父类的Designated Initializer初始化子类对象

    TestView *testView = [[TestView alloc] initWithFrame:CGRectZero];

    调用顺序如下:
    1)UIView的initWithFrame
    2)NSObject的init
    此时有问题,TestView的Designated Initializer没有被调用

    而测试1之所以没问题,是因为UIView遵循了原则1和原则2。

    好,现在我们将TestView修改为遵循原则1和原则2。

    - (id)initWithFrame:(CGRect)frame andName:(NSString *)name
    {
        if (self = [super initWithFrame:frame])
        {
            self.name = name;
        }
        return self;
    }
    
    - (id)initWithFrame:(CGRect)frame
    {
        return [self initWithFrame:frame andName:@""];
    }

    测试1:使用子类的Designated Initializer初始化子类对象

    TestView *testView = [[TestView alloc] initWithFrame:CGRectZero andName:@""];

    调用顺序如下:
    1)TestView的initWithFrame:andName:
    2)UIView的initWithFrame
    3)NSObject的init
    此时没问题,3个类的Designated Initializer都被调用了

    测试2:使用父类的Designated Initializer初始化子类对象

    TestView *testView = [[TestView alloc] initWithFrame:CGRectZero];

    调用顺序如下:
    1)TestView的initWithFrame
    2)TestView的initWithFrame:andName:
    3)UIView的initWithFrame
    4)NSObject的init
    此时没问题,3个类的Designated Initializer都被调用了

    原则3.你可以不自定义Designated Initializer,但需要通过重写父类的Designated Initializer,来调用父类的Designated Initialzier

    - (id)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame])
        {
            self.name = name;    //注意这里的name不是通过初始化函数传递进来的
        }
        return self;
    }

    原则4.如果有多个Secondary Initializers(次要初始化器),它们之间可以任意调用,但最后必须指向该类的Designated Initializer。而且在Secondary Initializer内不能直接调用父类的初始化器。注意重写父类的Designated Initializer的也算是Secondary Initializer

    //Super Override
    - (id)initWithFrame:(CGRect)frame
    {
        return [self initWithFrame:frame andName:@""];
    }
     
    //Designated Initializer
    - (id)initWithFrame:(CGRect)frame andName:(NSString *)name
    {
        if (self = [super initWithFrame:frame])
        {
            self.name=name;
        }
        return self;
    }
     
    //Instance Secondary Initializer
    - (id)initWithName:(NSString *)name
    {
        return [self initWithFrame:CGRectZero andName:name];
    }
     
    //Instance Secondary Initializer
    - (id)initWithName2:(NSString *)name
    {
        return [self initWithName:name];
    }
     
    //Class secondary initializer
    + (id)testViewWithName:(NSString *)name
    {
        TestView *testView=[[TestView alloc] initWithFrame:CGRectZero andName:name];
        return testView;
    }

    可以看到方法initWithName2调用的顺序是initWithName2–>initWithName–>initWithFrame:andName:,最后指向了Designated Initializer。
    同时需要注意的是,Secondary initializers不仅可以是实例方法,也可以是静态方法,如testViewWithName:

    还有一个问题是为什么Secondary Initializer内不能直接调用父类的初始化器?
    我们要明确,调用父类的Designated Initializer的那个方法就是子类的Designated Initializer,Designated Initializer有且只有1个,所以即使有多个初始化函数,也要保证只能有一个是Designated Initializer

    P.S.

    使用 NS_DESIGNATED_INITIALIZER 来显式指定 Designated Initializer,参照《Adopting Modern Objective-C》

    - (instancetype)init NS_DESIGNATED_INITIALIZER;

    参考链接

    正确编写Designated Initializer的几个原则

  • 相关阅读:
    对于字符串的重复字符的去除
    487-3279
    队列设计(转)
    动态创建JS
    前端技巧:禁止浏览器static files缓存篇(转)
    cas与NGINX整合(转)
    秒杀场景下MySQL的低效(转)
    html禁用缓存
    MD5随机盐值生成法
    SVN There are unfinished transactions detected
  • 原文地址:https://www.cnblogs.com/chenyg32/p/4870303.html
Copyright © 2020-2023  润新知