【问题标题】:iOS: UIView subclass init or initWithFrame:?iOS:UIView 子类 init 或 initWithFrame:?
【发布时间】:2011-10-30 21:38:30
【问题描述】:

我创建了一个具有固定框架的UIView 的子类。那么,我可以覆盖init 而不是initWithFrame: 吗?例如:

- (id)init {
    if ((self = [super initWithFrame:[[UIScreen mainScreen] bounds]])) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

-initWithFrame: 的 Xcode 文档说:“如果您以编程方式创建视图对象,则此方法是 UIView 类的指定初始化程序。子类可以覆盖此方法以执行任何自定义初始化,但必须调用 super在实施之初。”

“指定初始化器”是什么意思?

【问题讨论】:

    标签: ios uiview uikit subclass init


    【解决方案1】:

    指定的初始化器是所有其他初始化器必须调用的初始化器。 UIView 和子类有点不寻常,因为它们实际上有两个这样的初始化程序:-initWithFrame:-initWithCoder:,这取决于视图的创建方式。如果你在代码中实例化视图,你应该覆盖-initWithFrame:,如果你从笔尖加载它,你应该覆盖-initWithCoder:。或者,您可以将代码放在第三种方法中并覆盖这两个初始化程序,以便它们调用您的第三种方法。事实上,这通常是推荐的策略。

    因此,例如,您可以创建一个 UIView 子类 ClueCharacter,它有自己的初始化方法:-initWithPerson:place:thing:。然后,您可以像这样创建视图:

    对象-C:

    ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:@"Col. Mustard"
                                                             place:kInTheStudy
                                                             thing:kTheRope];
    

    斯威夫特:

    var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)
    

    这很好,但是为了初始化对象的 UIView 部分,你的方法必须调用指定的初始化器:

    对象-C:

    -(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
    {
        if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
            // your init stuff here
        }
    }
    

    斯威夫特:

    func init(name: String, place : CluePlace, thing : ClueWeapon)
    {
        if (self = super.init(CGRectMake(0, 0, 150, 200))) {
           // your init stuff here
        }
    }
    

    如果你想调用你的子类的初始化器-init,只要你在实现中调用-initWithFrame:就可以了。

    【讨论】:

    • 我的理解是只有一个初始化器(指定初始化器)应该1)调用超类的指定初始化器,并且2)包含初始化逻辑。任何其他初始化程序都应该调用指定的初始化程序(在这种情况下:[self initWithPerson:nil place:nil thing:nil])。 进一步,超类的指定初始化器应该被重写以调用新的指定初始化器。
    • @LeeFastenau 通常只有一个指定的初始化程序是正确的,但并非总是正确。如上所述,可以使用-initWithFrame:-initWithCoder: 来初始化视图,并且它们都不会调用另一个。所以在视图的情况下实际上有两个指定的初始化器。另请参阅-initWithFrame: 的文档。
    • 使用自动布局和约束只需创建自己的初始化并调用 super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
    【解决方案2】:

    UIView 中调用[super init] 完全等于[super initWithFrame:CGRectZero]

    【讨论】:

    • 但请注意,在 Swift 中,如果您有一个带有指定初始化程序的 UIView 子类 - 调用 super.init() 会导致编译错误 Must call a designated initializer of the superclass 'UIView',而调用 super.init(frame: .zero) 可以正常工作。跨度>
    【解决方案3】:

    在具有多个初始化程序的 Objective-C 类中,指定的初始化程序是执行有意义工作的初始化程序。所以,通常你有一个带有几个初始化器的类,比如:

    - (id)initWithRect:(CGRect)someRect;
    
    - (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour;
    
    - (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
                 linkTo:(id)someOtherObject;
    

    在这种情况下,您通常(但并非总是)说第三个是指定的初始化程序,并将其他两个实现为例如

    - (id)initWithRect:(CGRect)someRect
    {
        return [self initWithRect:someRect setDefaultColour:NO];
    }
    
    - (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
    {
        return [self initWithRect:someRect setDefaultColour:setDefaultColour
                       linkTo:nil];
    }
    

    如果一个类只有一个初始化器,那么它就是指定的初始化器。

    在您的情况下,要遵循最佳实践,您应该实现initWithFrame: 以及一个普通的init:,它以您的正常尺寸调用initWithFrame:。通常的约定是您可以在子类中对init 添加新的变体,但不应该带走任何变体,并且您始终在指定的初始化程序中进行实际的初始化工作。这允许来自父类的任何初始化方法,您不提供仍然的新实现,以与您的子类一起正常工作。

    【讨论】:

    • 您说得对,指定的初始化程序通常是参数最多的初始化程序,但事实并非如此。如果一个类有多个初始化器,类文档(或至少是头文件)应该说明哪一个是指定的初始化器。
    • 我实际上认为这可能是造成混乱的原因;在文档中指出哪个是指定的初始化器,这听起来像是你应该推断出比你在类只有一个或两个初始化器时更深的东西。另一方面,采用始终标记指定初始化程序的策略显然是正确的记录方式。
    • 我不确定我是否遵循...只有两种方法可以确定指定的初始化程序:阅读代码或阅读文档。如果代码不可用,文档就是你所拥有的。将指定的初始化程序设置为具有最多参数的初始化程序可能是一个很好的约定,但目前在您尚未编写的代码中还不能指望它。
    • 是的,我已经更新了我的答案,因为我并不是说“大多数参数 = 指定的初始化程序”。我刚才的评论是,如果您阅读具有单个初始化程序的类的文档,您经常会看到它被描述为指定初始化程序,因为编写它的人都是谨慎而彻底的。但是,如果您不了解指定的初始化程序是什么,那么即使有人将“指定”一词编辑掉,您的处境也不会更糟。我不认为他们应该这样做,因为我是一个带背带的人,我只是在说明这一点。
    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多