关于init和initWithframe

371 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

第一个注意事项

init会调用initWithframe:CGRectZero,所以使用重写initWithframe就行了

第二个注意事项:不要在initdealloc中使用self.property

在init中,进行初始化首选会调用 self = [super init](注意这里只是子类调用了父类的init方法,并不是把父类返回给子类的selfself是对象指针,super只是个标识符,跟self不是一种类型的东东,这里self是子类的对象指针,哪怕调用到了父类的init方法,父类init中的self还是指向子类的对象,因此在父类中调用到了子类的重载方法); 即子类先调用父类init方法,如果父类在init中使用了 self.property,一旦子类重写了 self.property的该方法,那么由于多态父类中调用的其实是子类的accessor方法(即self.propert方法),而此时只是在进行父类初始化,子类初始化还未完成,调用子类的accessor可能会造成崩溃。现实中更多的是某个方法被少调用一次,出现逻辑错误。

在销毁子类对象时,首先是调用子类的dealloc,最后调用父类的dealloc(这与init相反,在ARC中不需要我们手动调用[super dealloc], 系统会在子类dealloc的最后自动调用父类dealloc)。如果在父类dealloc调用了accessor且该accessor被子类重写,就会调用到子类断点accessor。但此时子类已经释放,就会出现错误甚至崩溃。

我们来看下代码: 父类

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        _name = @"222";
        NSLog(@"parent init:%@", self.name);
    }
    return self;
}

- (void)setName:(NSString *)name {
    _name = name;
    
    NSLog(@"parent set:%@", self.name);
}

- (void)dealloc {
    NSLog(@"parent dealloc:%@", self.name);
}

子类

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        _arr = [[NSMutableArray alloc] initWithObjects:@"arr1", @"arr2", nil];
        NSLog(@"child init:%@", self.name);
        self.name = @"111";
    }
    return self;
}

- (void)setName:(NSString *)name {
    [super setName:name];
    
    NSLog(@"child set:%@", self.name);
    NSMutableArray *arr = [[NSMutableArray alloc]init];
    [arr addObject:self.arr];
    NSLog(@"%@", arr);
    NSLog(@"arr:%@", self.arr);
}

- (void)dealloc {
    NSLog(@"child dealloc:%@", self.name);
}

当我们使用子类的时候,就会崩溃在子类的set方法。所以initWithframe里初始化应该使用下划线实例变量。