50802


Objective-Cになります。  
AppDelegate.mには処理として以下を用意してあります。  
イメージとしてはタブで画面切り替えでき、その画面は`UISplitViewController`となっています。

[code]

    _tabAViewController = [[AViewController alloc]initmake];
    _tabBViewController = [[BViewController alloc]initmake];
    baseViewController.viewControllers = @[_tabAViewController, _tabBViewController ];①
    
[/code]

`AViewController`、`BViewController`クラスでは`CommonSplitViewController`クラスを継承しており、  
`CommonSplitViewController`クラスは`UISplitViewController`を継承しています。  
また、`baseViewController`は`UITabBarController`を継承したクラスです。

`AViewController`、`BViewController`クラスでは以下を用意しています。

[code]

    - (id)initmake
    {
      listView = [[ListView alloc]init];
      listView.showView = self.view;②
    　　　 return [self init];  // 12/3追記
    }
    - (void)viewDidLoad
    {
        [super viewDidLoad];③
        ～～
    }
    
[/code]

`CommonSplitViewController`クラス処理として以下を用意しています。

[code]

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
        }
        return self;
    }
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // 区分線を追加
        UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(300, 0, 1, 600)];
        [self.view addSubview:lineView];④
    }
    
[/code]

①を実施すると、②が呼ばれ、その後③がよばれます。  
③から継承元の`CommonSplitViewController`クラスの`viewDidLoad`が実施されるのは  
理解できるのですが、さらに④→③→④…が繰り返されてしまいます。  
④の処理でなぜ③が呼ばれるのでしょうか？

また、②実行で③が呼ばれることもいまいち理解できていません…

`ListView`のクラスですが`BaseTableView`クラスを継承しています。  
`BaseTableViewはUITableView`を継承したクラスになっております。

[code]

    @interface ListView : BaseTableView <UITableViewDelegate, UITableViewDataSource> {
    }
    @property (nonatomic) UIView *showView;
    
[/code]


CC BY-SA 4.0
2018-11-30T11:17:18.887
50788
2018-12-04T05:22:34.437
2018-12-03T02:26:57.800
12842
12842
question
objective-c
addSubviewで繰り返し、viewDidLoadが呼ばれる


直接の回答になるか解りませんが、`initmake`で`self = [super init];`を呼んでいないのが気になります。  
このため、メソッド`initmake`中での`self`に何が入っているか解りませんので、`listView.showView =
self.view;`を実行しても`listView.showView`に代入される`view`インスタンスは不定になってしまいませんでしょうか？

名前も、`initmake`と継承元のイニシャライザーと名前を変える必要は特に感じられないので、`initmake`部を以下の様に書き替えるとどうなりますでしょうか？

[code]

    - (instancetype) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
        if (self) {
            listView = [[ListView alloc] init];
            listView.showView = self.view;
        }// end if can super is allocate and initialized
    
        return self;
    }// end initWithNibName
    
[/code]

`CommonSplitViewController`もコンストラクター(`init`の中で`self = [super
init...];`を行っていないのであれば、同様に、と記述し、`// メンバーの初期化`の部分で必要なメンバーの初期化を行って下さい

[code]

    - (instancetype) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
        if (self) {
            // メンバーの初期化
        }// end if can super is allocate and initialized
    
        return self;
    }// end initWithNibName
    
[/code]

呼び出す際は、

[code]

    _tabAViewController = [[AViewController alloc] initWithNibName: nil bundle: nil];
    _tabBViewController = [[BViewController alloc] initWithNibName: nil bundle: nil];
    
[/code]

ご自身でデザインした`xib`ファイルがあるのでしたら、１番目の引数の`nil`は該当する`xib`ファイル名にして下さい。

12/4 補足

上記回答で解決したとのこと、おめでとうございます。  
原因は、`initmake`というメソッドにあるのではなく、コンストラクターチェーン（造語）を呼んでいないことが原因です。

あるクラス`Base`を継承した、派生クラス`Derived`を作成するとき、Derivedのコンストラクターはその中で、`Base`クラスのコンストラクターを呼び出してあげる必要があります。  
そうすることで、`Base`クラスも`Root`クラスの派生クラス出会った場合、`Base`クラスのコンストラクターが中で`Root`クラスのコンストラクターを実行し・・・
という風に、最も大本であるクラスまでコンストラクターの呼び出しを連鎖していきます。  
その結果、継承元のクラスの自分にはあずかり知らない変数や、インスタンスが初期化され、こうして晴れて自分が定義した派生クラスが使える様になるのです。

@kankoさんが定義した、`initmake`というコンストラクターには、この「継承元のクラスのコンストラクターを呼び出す」という記述が抜けていたため、動作が不定となってしまい、それが「たまたま」③→④を繰り返し呼び出してしまうと言う再現性のある不具合となって現れたわけです。

`Objective-C`では一般的にコンストラクターは`init`で始まり、必要なパラメーターがあれば、Withパラメーターの意味と続けていくという暗黙のルールと、継承元のクラスのコンストラクターを呼び出すので、継承元のコンストラクターと似た名前で必要なパラメーターを受けとって、継承元のコンストラクターを呼び出す事になります。  
その基本の形が、

[code]

    - (instancetype) init
    {
        self = [super init]; // まず最初に継承元のクラスに初期化を依頼する
        if (self) { // selfがnil(初期化失敗)でなかったら
            // 自分自身のメンバー変数の初期化、継承元クラスの挙動を更に修正等するための設定
        }
    
        retrun self; // 初期化が終わった自分自身のインスタンスを返す
    }// end init
    
[/code]

という形になります。  
あとは、これに、どんな変数を受けとるか？が引数リストとして続いていきます。その一例が、`xib`ファイルがどんな名前(`Name`)で、何処にあるか？(`bundle`)を引数として要求する`-
(instancetype) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle
*)nibBundleOrNil`な訳です。


CC BY-SA 4.0
2018-12-01T05:42:03.377
50802
2018-12-04T05:22:34.437
2018-12-04T05:22:34.437
14745
14745
50788
answer