I'm trying to understand how the compiler works around this.
I've read that subclass designated initializers must:
First initialize all properties of the subclass.
Then call super.init().
I don't understand why the subclass must initialise first and the initialise the superclass. How is this possible if the superclass doesn't exist yet?
Thanks!
On https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215 under "Two-Phase Initialization", it is explained extensively
The main reason is safety. Each property must first assign a value -- and then only when the entire hierarchy has done that, can you start reading values.
The reason super.init() comes after subclass property initialization is so that super.init() can call a method that might be overridden in your class. When it does that, we know that you have already given your properties an initial value.
Related
How do I ensure objects passed to an initializer are copied, rather than only on setting those attributes later?
Using #NSCopying, Apple says we can achieve copy-property-like behavior. Per default, the attribute is only assigned though, without calling the setter which does the copying.
This is potentially dangerous as I want to rely on the property being immutable and not being modified without me knowing. (think of getting an NSMutableString instead of NSString - copying would give me an immutable instance).
Use #NSCopying while declaring the property.
From within the initialiser call self.propertyname = newValue, so that the setter gets called and copying is done.
To know when the value is being modified from outside of the class, implement the "set" observer (which also requires to implement get as well).
Each view class has multiple init methods - ones already included as part of UIView, and then additional ones - and each of them set up the same elements in the same way. I therefore usually have them all running a [self initialSetup]; method, which includes the setting up of all of these elements.
The problem i've ran into is that if a subclass also has an initialSetup method, it would override the superclass initialSetup method, and thus the superclass would have to have the method be public in order to still function. This causes problems with organisation, as the method should never be called other than from init, so has no reason to be public.
You've hit upon a problem that there's no perfect fix for. What you'd ideally have is a method that can't be subclassed in the normal sense, that's accessible only to instances of that exact type of class.
Where this is a risk, the normal practice seems to be to incorporate the class name into the setup method. So instead of initialSetup you'd have something like myViewSubclassInitialSetup.
You can also add something like this at the top of your method:
NSAssert([self isMemberOfClass:[ThisClass class]],
#"IniitalSetup called by sub- or superclass")
Then your debug builds will raise an exception if a subclass or superclass ends up calling your init method. That'll give you a place for a breakpoint and a stacktrace that should allow you to find the problem very quickly.
It won't add any code to your release builds.
Change the name of initialSetup to something like initialSetupClassName - subclasses, even if they accidentally used the same pattern, would not use the same name as they had a different class name.
You can also use an "_" prefix for private methods you would rather not be called, but the subclasser may do that also.
It sounds like you are missing a designated initializer. Designate one initializer as the official one that actually performs the setup, and have all the others just call that with some degree of customization. Usually the designated initializer will be the one with the most detail — for example, if you have init, initWithName:, initWithName:age: and initAsFiveYearOldNamed:, the designated initializer will be initWithName:age: and the other initializers would just call that method with the arguments filled in appropriately.
Unfortunatly Objective C doesn't provide a way to achieve that in a "clean" way. The ideal solution would be a protected method. But that's not possible in Objective C
Apple had this problem, when they were creating the UIGestureRecognizer. There were some methods they really didn't want to get called by somebody, but which had to be overwritten by subclasses. The way they chose to deal with this, was to create a seperate header file (UIGestureRecognizerSubclass.h), that contains a category to the original UIGestureRecognizer with those "protected" methods. The additional header is only to be imported by subclasses (i.e. for subclassing purposes). See UIGestureRecognizer Class Reference for some details.
Of course that doesn't prevent anybody from misusing the additional header file, but at least it clearly states your intention and keeps your code well structured. Also you won't be "bothered" by autocompletion for the additional methods, when just using the class.
Personally I only use an additional header, if it is extremely important that nobody calls it directly. In most cases I think it's ok to use public methods and make a note for what it's inteded. The iOS Framework also has many of these cases. F.e. many methods of UIViewController's viewDidLoad etc.
I've defined two classes in an m file, the first subclassing UIView and the second UIViewController. The UIViewController is instantiated at some point, and the vc is who instantiates my first class.
the first class implements the touchesEnded method, to simulate a button. when the touchesEnded method is fired in the first class, is it possible to easily call a method defined in the 2nd class, without going into delegates and such?
I tried playing with selectors with no luck
is it possible to easily call a method defined in the 2nd class
Yes, assuming that you are creating an instance of the second class and calling the method on that instance.
Regardless of whether the two classes are subclasses of the same type, or in the same or different files, you need a reference to an instance of that class to call a method on it, or force it to perform a selector.
The proper OO way to do this is with delegates, but you could theoretically do something like pass a reference to view 2 into view 1 when you create the views. If you create them in IB you could create outlets so they reference each other that way.
In short: Yes, it is possible and easy to do, but I can't give you too much in terms of specific code without a more specific example of your situation
I've read google's and apple's code guide, they both access instance variables without self. call(getter and setter) inside the method implementation even though they have declared a property for that instance variable.That's why?
In my opinion, using self. call to set and get instance variables inside the method implementation of class makes it easier to manager retain count.
Is there any caveat to use getter and setter inside class?
It depends. You should always use the accessor in normal use.
However for init and dealloc methods, you should instead use the direct ivars to release and set variables. That's because the setters can have side effects that are not good to trigger during class initialization or deallocation.
In practice using an accessor as part of init probably will not cause an issue. But I have seen a number of real world crashes where a custom setter was not expecting nil and so use of the accessor in dealloc crashed the app. Even if it didn't crash it could be doing a lot of pointless work since the class was about to die.
As an Objective-C beginner, I'm very confused about the init function, and how and when to override it. So here are few questions :
Apparently, it works fine when the init function is not overridden, so is it just a good practice to do it? If it is, then is it a very bad practice not to do it?
Let's assume I'm overriding the function because I have to assign a default value to a variable. Do I have to allocate and initialize all the other ivars, including IBOutlets?
Please note I'm aware of the syntax :
if ((self = [super init]))
{
_foo = [[Bar alloc] init];
}
return self;
Per "Initialization":
A class generally implements an initializer for its objects, but is not required to. If a class does not implement an initializer, Cocoa calls the initializer of the nearest ancestor of the class. However, subclasses often define their own initializer or override an initializer of their superclass to add class-specific initializations. If a class does implement an initializer, it should invoke an initializer of its superclass as the first step. This requirement ensures a series of initializations for an object down the inheritance chain, starting with the root object. The NSObject class declares the init method as the default object initializer, so it is always invoked last but returns first.
As this says, you override your superclass's designated initializer when you need to do some initialization after it's done with its initialization. Don't need to do that? Then you don't need to override.
When your object is instantiated from a NIB, -init is not called. Instead, your newly allocated object will receive an -initWithCoder: or -initWithFrame: message depending on the object's type. The NIB loading process sends your object -awakeFromNib after it and all the other NIB-created objects it references have been established. This lets you avoid overriding -initWithCoder:/-initWithFrame: when you wish to do some configuration after the NIB has been loaded. If you can do what you want to do by overriding -awakeFromNib rather than an initializer, you should do that.
See also "Multiple Initializers", which explains the "designated initializer" concept and how different classes can have different designated initializers, and "Allocating and Initializing Objects" for a less friendly but more in-depth description of the allocation and initialization conventions adopted by Objective-C.
There is no need to override -init (or the designated initializer) unless you need to do class-specific initialization.
You do not need to (nor should you) allocate or initizlize IBOutlets. Objective-C will automatically initialize all instance variables including IBOutlets to 0 (nil).
no not at all .... just initialize what ever you want ... again IBOutlets are not initialized ... you only set them to nil when there is a memory warning or when ever you want to break the link ....