总结一下学习Designated Initializer的过程

来源:互联网 发布:淘宝动态评分怎么提升 编辑:程序博客网 时间:2024/05/30 02:25

其实我也是在看别的代码时无意中搜了一下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
原创粉丝点击