总结一下学习Designated Initializer的过程

其实我也是在看别的代码时无意中搜了一下initWithCoder这个方法,结果就引导 Designated Initializer那里去了。既然来了就弄懂吧,反正早晚都是要了解的,你看,这就是没有系统学习的方式,看到哪学到哪,不过这有一个好处,就是知道为什么缘由,不像有些教程那样,没有任何前戏直接就讲,我受不了。
在学些过程中主要参考了下面的两篇文章:
正确编写Designated Initializer的几个原则
How To: Objective C Initializer Patterns
这两篇文章解释的很清楚,尤其是how to那篇,还形象的画了调用的顺序图,看起来更加直观一些,还真是肯花时间啊,不过我想他画完图之后自己也会更加深刻了,那句话怎么说来着:送人玫瑰,自己手也香。所以我也来好好的总结一下,顺便加深自己的印象。
其实我要做的就是把他们在文章说的用代码验证一下。
我创建了一命令行的工程,在里面使用了xtrace,使用的目的是跟踪打印各个类的调用顺序。使用xtrace的方法就不在这里说明了,搜吧。
主要文件见下面:

//
//  main.m
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/23/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "TestInitView.h"
#import "SubTestInitView.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...

        [Xtrace includeMethods:@"^init"];
        [Xtrace traceClass:[NSView class]];
        [Xtrace traceClass:[TestInitView class]];
        [Xtrace traceClass:[SubTestInitView class]];
        [Xtrace traceClass:[NSObject class]];

        TestInitView *tv = [[TestInitView alloc] initWithFrame:CGRectZero andName:@" "];
        tv = tv;

        TestInitView *tv2 = [[TestInitView alloc] initWithFrame:CGRectZero];


        NSLog(@"Hello, World!");
        NSLog(@"i am new %@.",[NSDate date]);



    }
    return 0;
}
//
//  TestInitView.h
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/24/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface TestInitView : NSView


//Designated Initializer
- (instancetype)initWithFrame:(NSRect)frameRect andName:(NSString *)name;
@end
//
//  TestInitView.m
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/24/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import "TestInitView.h"

@implementation TestInitView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
}


//Designated Initializer
- (instancetype)initWithFrame:(NSRect)frameRect andName:(NSString *)name
{
    if(self == [super init])
    //if(self == [super initWithFrame:frameRect])
    {
        //self.name = name;
    }
    return self;
}

/*
//Overriding super class Designated Initializer
- (id)initWithFrame:(NSRect)frameRect
{
    return [self initWithFrame:frameRect andName:@" "];
}
 */


@end
//
//  SubTestInitView.h
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/24/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import "TestInitView.h"

@interface SubTestInitView : TestInitView

@end
//
//  SubTestInitView.m
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/24/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import "SubTestInitView.h"

@implementation SubTestInitView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
}

@end

此时此刻的文件实现目的是看看重新定义了子类的Designated Initiazlizer后,(1)没有override父类的Designated Initializer 和(2)没有在子类新定义的Designated Initializer中调用父类的Designated Initializer的情况下,使用子类Instance一个变量是什么样的顺序,原谅我在中文中加载了单词,原因是我也不知道该怎么翻译好。Instance的意思是说定义一个指定类型的变量,这个描述的话是在有点绕口,就当它在文中是个宏吧。

简单介绍一下这几个文件的关系,其实也能看出来TestInitView是继承于NSView的,在TestInitView.m中定义了一个新的Designated Initializer,但是却没有override父类的Designated Initializer,因为被我注释掉了。并且新定义 Designated Initializer也没有调用父类的Designated Initializer,它存在两个问题。
新定义的Designated Initializer是

//Designated Initializer
- (instancetype)initWithFrame:(NSRect)frameRect andName:(NSString *)name
{
    if(self == [super init])
    //if(self == [super initWithFrame:frameRect])
    {
        //self.name = name;
    }
    return self;
}

SubTestInitView在这时没有使用。
main.m中使用TestInitView 初始化了2个实例,但是使用的方法不一样。

 TestInitView *tv = [[TestInitView alloc] initWithFrame:CGRectZero andName:@" "];

是企图通过使用自己Designated Initializer来初始化一个实例变量。

还有一个,它企图使用父类的Designated Initializer来实例化一个变量

TestInitView *tv2 = [[TestInitView alloc] initWithFrame:CGRectZero];

下面看看他的调用顺序,注意他们都是错误的。第1个实例化的顺序错了,第2个就更错了,缺少很多中间的过程。

2016-09-24 17:11:37.428 my-test-designate-initializer[2331:216606] Xtrace: Tracing NSObject will not trace all classes
| From: main
| [<TestInitView 0x10020fc00> initWithFrame:{{0, 0}, {0, 0}} andName:@" "]
|   [<TestInitView 0x10020fc00>/NSView init]
|     [<TestInitView 0x10020fc00>/NSView initWithFrame:{{0, 0}, {0, 0}}]
|       [<TestInitView 0x10020fc00>/NSResponder init]
|       -> <TestInitView 0x10020fc00> (init)
|     -> <TestInitView 0x10020fc00> (initWithFrame:)
|   -> <TestInitView 0x10020fc00> (init)
| -> <TestInitView 0x10020fc00> (initWithFrame:andName:)
| From: main
| [<TestInitView 0x10030caf0>/NSView initWithFrame:{{0, 0}, {0, 0}}]
|   [<TestInitView 0x10030caf0>/NSResponder init]
|   -> <TestInitView 0x10030caf0> (init)
| -> <TestInitView 0x10030caf0> (initWithFrame:)
2016-09-24 17:11:37.429 my-test-designate-initializer[2331:216606] Hello, World!
2016-09-24 17:11:37.431 my-test-designate-initializer[2331:216606] i am new 2016-09-24 09:11:37 +0000.
Program ended with exit code: 0

下面来奉上正确的代码。

首先需要修改TestInitView.m
1.在新定义的Designated Initializer中调用父类的Designated Initializer;
2.重新定义父类的Designated Initializer,并且调用1中新定义的Designated Initiazlier;
修改后的代码为:

//
//  TestInitView.m
//  my-test-designate-initializer
//
//  Created by ChrisBluez on 9/24/16.
//  Copyright © 2016 ChrisBluez. All rights reserved.
//

#import "TestInitView.h"

@implementation TestInitView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
}


//Designated Initializer
- (instancetype)initWithFrame:(NSRect)frameRect andName:(NSString *)name
{
    //if(self == [super init])
    if(self == [super initWithFrame:frameRect])
    {
        //self.name = name;
    }
    return self;
}


//Overriding super class Designated Initializer
- (id)initWithFrame:(NSRect)frameRect
{
    return [self initWithFrame:frameRect andName:@" "];
}



@end

然后再重新运行,查看console中的打印结果

2016-09-24 17:20:19.942 my-test-designate-initializer[2356:223925] Xtrace: Tracing NSObject will not trace all classes
| From: main
| [<TestInitView 0x100200710> initWithFrame:{{0, 0}, {0, 0}} andName:@" "]
|   [<TestInitView 0x100200710>/NSView initWithFrame:{{0, 0}, {0, 0}}]
|     [<TestInitView 0x100200710>/NSResponder init]
|     -> <TestInitView 0x100200710> (init)
|   -> <TestInitView 0x100200710> (initWithFrame:)
| -> <TestInitView 0x100200710> (initWithFrame:andName:)
| From: main
| [<TestInitView 0x100207d60> initWithFrame:{{0, 0}, {0, 0}}]
|   [<TestInitView 0x100207d60> initWithFrame:{{0, 0}, {0, 0}} andName:@" "]
|     [<TestInitView 0x100207d60>/NSView initWithFrame:{{0, 0}, {0, 0}}]
|       [<TestInitView 0x100207d60>/NSResponder init]
|       -> <TestInitView 0x100207d60> (init)
|     -> <TestInitView 0x100207d60> (initWithFrame:)
|   -> <TestInitView 0x100207d60> (initWithFrame:andName:)
| -> <TestInitView 0x100207d60> (initWithFrame:)
2016-09-24 17:20:19.944 my-test-designate-initializer[2356:223925] Hello, World!
2016-09-24 17:20:19.946 my-test-designate-initializer[2356:223925] i am new 2016-09-24 09:20:19 +0000.
Program ended with exit code: 0

其中,NSResponder是NSView的父类,NSObject是NSResponder的父类。
对比两种结果就知道哪里有错误了。错误的实现Desigated Initializer和相关的Initializer带来的后果就是1.漏掉了某些期望被调用的Initializer,2调用顺序错误,3就是1和2都有。

最后不要脸的补充一下,这篇文章是目前我写的比较有技术含量的一篇,它集代码,运行结果,参考链接为一体。
再接再励。
我现在老丈人家,等会吃完饭跟媳妇回家再继续研究。
你们等着,我要学ios挣钱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值