Consider the following class hierarchy in Swift:
GMSMarker is a class provided by the GoogleMaps library. It has two public initializers (one designated, one convenience).
MapItem, ClusterItem and Cluster are part of my model. I don't want to allow construction of MapItem objects; only ClusterItems and Clusters. Since Swift doesn't have abstract classes, having a private initializer would be sufficient for my purposes. However, given that MapItem inherits from GMSMarker with a convenience constructor, I can't simply override the designated initializer as private.
Given the rules of initializer inheritance in Swift, the only way that I've been able to prevent construction of the MapItem class is by declaring a new private initializer with a different signature so that the base class initializers aren't inherited.
class MapItem : GMSMarker {
private init(dummyParam: Int) {
super.init()
}
}
So far so good. Now, when trying to create my initializer for the ClusterItem class, I run into a problem.
class ClusterItem : MapItem {
weak var post: Post?
init(post: Post) {
self.post = post
super.init(dummyParam: 0)
}
}
I receive the following compiler error on the line that calls the super.init(): 'MapItem' does not have a member named 'init'.
If I omit the call to super.init, I receive the following error: Super.init isn't called before returning from initializer.
Is there any way of accomplishing what I want in swift. I would like to keep the inheritance hierarchy as is if possible because MapItem contains some common implementation code for ClusterItem and Cluster. Furthermore, I would like to be able to reference both ClusterItems and Clusters using a collection of MapItems.
There are various ways to solve this issue:
Declare the initializer as internal, that will give you module access and your subclass will be able to call it.
Declare the classes in the same file, then private won't be an issue anymore (private enables access from the same file).
Instead of abstract classes, you can make MapItem a protocol and ClusterItem and Cluster could inherit from GMSMarker directly. However, this solution may not be good depending on your exact needs.
Related
In my flutter project, I have the following abstract class.
abstract class Storage {
static method1{}
static method2{}
...
}
Then I define other classes that extend to Storage but each child class implements some of the methods defined (with empty body) in the Storage class.
class StorageA{
static method1{ print("1") }
}
class StorageB{
static method2{ print("2") }
}
My goal is to be able to call any of these static method by using the Storage namespace, however, I want to invoke the overridden methods in the child classes. For example, when I call Storage.method1 it should print 1. This is a very simplified example but I normally have bunch of methods and I want to group these methods into different classes that extend to Storage. But at the same time i want to access all the overridden methods with Storage namespace. Currently when I do Storage.method1 compiler picks up the function defined in Storage because it has an empty body. If i remove the body and turn it into function declaration, then I can't define the function as static. So, how can I reach my goal here?
Is combining everything into a single Storage class and defining the methods as static the only solution here?
I am trying to understand the syntax for initializers and how they work. I am reading the swift documentation and I am having a hard time understanding how they work in a specific example I am working on. I'm following a tutorial to learn about core data but I do not want to continue through the project until I understand how the initialization code works (or any other concept I do not understand).
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
Core Data: Note Entity Class
Core Data: Note Class Convenience Initializer
In the first image above I show the Note Entity Class that Core Data creates and in the second image I add a convenience init(). All the extra on the code in the extension of the Note class is my notes.
The first two comments on the extension of the Note class is what I have found out about how the entity class hierarchy which is that the super class is the NSMangedObject (super class) then the sub class is the Note entity class. To my understanding the NSMangedObject class has 3 initializers which are:
init() - Default Initializer
init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) - Designated Initializer
convenience init(context moc: NSManagedObjectContext) - Convenience Initializer
Then Note entity class the only thing I have is the convenience init(title: String, context: NSManagedObjectContext). For this initializer I understand how the title and creationDate are initialized but my question is on the self.init(context: context).
Question: To my understand reading the Swift documentation a convenience initializer cannot point to another convenience initializer which is what I think it's happening here?? I think that the default initializer from the Note class is pointing to the initializers from the super class of NSMangedObject. Can anyone provide me with some insight to understand what is happening.
A convenience initializer must call another initializer in self. That is all it is required to do. It must do that before saying self for any other purpose.
In the Node extension, the convenience initializer is doing that; it is starting out by calling self.init(context:).
That is legal because Node is an NSManagedObject subclass (representing an entity), so it inherits that initializer:
https://developer.apple.com/documentation/coredata/nsmanagedobject/1640602-init
That's all there is to know. The fact that init(context:) is a convenience initializer is not interesting. It's an initializer of self, that's all that matters.
Quote from The Swift Programming Language (Swift 3.1):
Custom initializers can be assigned an access level less than or equal to the type that they initialize. The only exception is for required initializers (as defined in Required Initializers). A required initializer must have the same access level as the class it belongs to.
If so, why does this code compile and work?
private class GoofyClass {
public init(mood: String) {}
public required init(isCrazy: Bool) {}
}
private let shock = GoofyClass(mood: "shocked")
private let crazy = GoofyClass(isCrazy: true)
In Swift, members of a class or struct with a less restrictive access level than the class/struct itself are automatically downgraded to the same level as the class/struct. I believe this is a deliberate design decision on the part of the language designers.
In your case, assuming the class is declared at the top level in the file (i.e. it is not nested inside another type), the inits you have declared public are, in fact, fileprivate.
The only exception is for required initializers (as defined in Required Initializers). A required initializer must have the same access level as the class it belongs to.
This is referring to the fact that you cannot make the access level of a required initialiser more restrictive than its class e.g.
open class Foo
{
internal required init() // error
}
The rule for access control for required initializer seems to be different than one that does not specify required. Why?
public class A {
// required init() must be public, why?
public required init() { }
}
public class B {
// init() does not need to be public, why?
init() { }
}
First, let's make the rule clear. It is not required for required initializers to be marked as public. It is only required that required initializers be as accessible as the class is. If your class is public, it's required initializers must also be public. If your class is internal, its required initializers must also be internal (technically, you could make it public, but that'd make no sense and generates a warning). And of course, if your class is private, the required initializer should also be private.
So, why?
There are two reasons here, but they require an understanding of what the required keyword is actually doing.
First of all, the required keyword is guaranteeing this class and all of its subclasses implement this particular initializer. One of the main reasons to make an initializer required is for protocol conformance, with the most popular example of this being NSCoding, which requires the init(coder:) initializer. So with that in mind, let's consider a class which is trying to implement this protocol:
public class MySwiftClass: NSObject, NSCoding {
// some implementations
// including the two requirements of the NSCoding protocol
}
Now, consider trying to use this:
let mySwiftObject = MySwiftClass(coder: aCoder)
We should be able to do this without problem, right? I mean, after all, MySwiftClass conforms to NSCoding protocol, and NSCoding protocol guarantees there will be an init(coder:) initializer.
But if you were allowed to mark init(coder:) as a lower access level than the class had, there would be a scope within which the class can be seen, but its required initializer could not be accessed... so despite knowing that this class conforms to a protocol with a required initializer or is inherited from a parent class with a required initializer, we'd somehow not be able to call that required initializer because for the scope we are in, it would appear to not exist.
The second reason is for subclassing itself.
Let's take this example parent class:
public class ParentClass {
required init() {}
}
We want the zero-argument initializer to be required. That means, if anything inherits from ParentClass, it must also be sure that the zero-argument initializer is implemented. But if we are allowed to let the required initializer to have a lesser scope than the class itself, then there is a scope within which we can see the class, but we cannot see the required initializer, so how can subclasses created in that scope manage to even know there is a required initializer that they must implement?
I'm new to coding, apologies for dumb question.
Am following a tutorial to build a note taking app using Swift in Xcode.
Within a class definition I have been defining methods using the keyword func myMethod etc. At one point the instructor decides to define a Class method (within the existing class) using class func myMethod.
Why would you do this?
Thanks in advance for any feedback.
By defining a class method it means that you don't need an instance of that class to use the method. So instead of:
var myInstance: MyClass = MyClass()
myInstance.myMethod()
You can simply use:
MyClass.myMethod()
The static (class) function is callable without needing an instance of the class available; it may be called without having to instantiate an object.
This can be useful for encapsulation (avoiding placing the function in the global namespace), or for operations that apply to all objects of a given class, such as tracking the total number of objects currently instantiated.
Static functions can be used to define a namespaces collection of related utility functions:
aDate = Utils.getDate()
aTime = Utils.getTime()
Another common use is for the singleton pattern, where a static function is used to provide access to an object that is limited to being instantiate only once:
obj = MySingleton.getInstance()
obj.whatever()
One answer is namespacing. If a function is only relevant to a certain class there is no need to declare the function globally.
This is Swift's take on static methods:
Static methods are meant to be relevant to all the instances of a class (or no instances) rather than to any specific instance.
An example of these are the animation functions in UIView, or the canSendMail function from MFMailComposeViewController.