Converting objective C alloc init to swift with required argument - objective-c

I am working on a Swift metawear (mbientlab.com) project and all the code examples are in Objective C so have to do lots of conversion. Following this blog post - http://projects.mbientlab.com/persistent-events-and-filters/ - I created the following class that inherits from MBLRestorable (which implements the NSCoder protocol):
class DeviceConfiguration:NSObject, MBLRestorable {
var pulseWithEvent:MBLEvent!
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.pulseWithEvent, forKey: "pulseWithEvent")
}
required init(coder aDecoder: NSCoder) {
super.init()
self.pulseWithEvent = aDecoder.decodeObjectForKey("pulseWithEvent") as MBLEvent
}
}
So far so good. Now I am converting the following Objective C to Swift:
[self.device setConfiguration:[[DeviceConfiguration alloc] init] handler:^(NSError *error) {
if (!error) {
// Programming successful!
}
}];
I try:
self.device.setConfiguration(MetawearConfig()) { error in
}
But get an error that it is missing the required argument "coder." Make sense to me that it would require that param on init but in the Objective C sample code/applications a coder obj is never passed in (and the compiler does not raise the same error).
The declaration for setConfiguration is:
- (void)setConfiguration:(id<MBLRestorable>)configuration handler:(MBLErrorHandler)handler;
What I am missing?

You're calling MetawearConfig(), but the only init you provide is init(coder aDecoder: NSCoder). If you want a non-parameter init, you must provide one:
init() {
// initialize your object
}

Swift classes do not by default inherit the superclass's initializers (See Swift language guide -> Initialization -> search for section "Automatic Initializer Inheritance"). NSObject's init() is a "designated initializer". Designated initializers are only inherited by a subclass if the subclass implements no designated initializers of its own. Since your DeviceConfiguration class implements a designated initializer init(coder:), it does not inherit designated initializers from its superclass. That's why DeviceConfiguration does not have an initializer with signature init().
Objective-C is different in that initializers are just methods in Objective-C and are always inherited.

The DeviceConfiguration in Objective-C would also have a different init with no parameters, inherited from NSObject. The code you're converting looks to be calling the no-parameter version rather than the NSCoder version.

Related

Can't call custom swift init from objective-c class

I have a custom class with a custom init written in swift. I need to call that init from an Objective-C class.
Swift
#objc public class MyClass: NSObject {
public init(configuration config: Data)
{
super.init()
// Do Stuff
}
}
Objective-C
[[MyClass alloc] initWithConfiguration:CONFIG];
But when I call the init from Objective-C the compiler complains that
No visible #interface for 'MyClass' declares the selector
'initWithConfiguration:'
What am I missing here?
You have to add #objc attribute to initializers too. Like this:
#objc public init(configuration config: Data)
{
super.init()
// Do Stuff
}
And after this, don't forget to re-build (CMD+B), otherwise Xcode will stupidly emit an error.

initialize swift class from objective c library

Hello I am currently importing a project from objective C to swift. The objective C project uses a custom library which in one of the classes it has a function that takes a Class object, which, when the data is loaded it initializes an id obj with the class(code below). On the objective C side this works great! I am using the same library on swift with a bridge header but unfortunately my object is nil once the data is loaded. I verified the data object used to initialize the object has content. The custom swift class also requires me to implement an init(data: Data) initializer when I add a custom init(), I am new to swift any help would be great! Thank you!
Objective C:
Library:
-(id) buildRetObject:(Class)clss fromData(NSData*) data{
id obj = nil;
if (data != nil) {
obj = [[clss alloc] initWithData:data];
}
}
Custom Object, is a subclass of another custom object, not in library but on main app:
Item.m:
-(id)init{
if ([super init]){
//do something
}
return self
}
Same Item object in swift:
//customClass super class is abstactCustomClass which is a sub abstract class of NSObject class, init functions are in here, the abstract class
class Item: customClass {
//variables
override init() {
super.init()
// do something
}
required init!(data: Data){
//fatalError(message here)
/*tried calling supers method
super.init(data:Data) gives error: fatal error: use of unimplemented initializer 'init(dictionary:)'*/
}
//methods
}
After a lot of digging I was finally able to figure it out, objective C knows from the abstract class what the initializers are, swift doesn't. So all I need to do was implement this initializers and it works.
required init!(data: Data){
super.init(data:Data)
}
override init(dictionary: [AnyHashable: Any]!){
super.init(Dictionary:dictionary)
}

Swift function overriding Objective-C method

I have an Objective-C method (declared in the class named "BaseViewModel" of my old framework) that is :
-(void) updateFromManagedObject:(__kindof NSManagedObject *)entity;
I want to use it in mutiple Swift classes. Each Swift class will use a particular subclass of NSManagedObject and inherits from "BaseViewModel". When i try to override this func like this :
override func updateFromManagedObject(entity: Person?) {
<#code#>
}
OR
override func updateFromManagedObject(entity: Animal?) {
<#code#>
}
the compiler returns :
Method does not override any method from its superclass
It only works with :
override func updateFromManagedObject(entity: NSManagedObject?) {
<#code#>
}
How can I use specifics inherited types of NSManagedObject ? (Maybe with a class Generic-Type ? I try but failed too :/ )
The point of overriding is that the subclass method is called instead of the superclass method when the receiver is an instance of the subclass. Therefore, the subclass method's parameters must handle at least all the parameters the superclass method can handle. So the subclass method's parameters' types must be the same or more general than the parameters' types for the superclass method it overrides.
You cannot overload Objective-C functions based on the argument type, as Objective-C doesn't support this kind of overloading. This is why only the NSManagedObject overriding works.
If you want different behaviour based on the entity type, an alternative would be to declare a Swift-only method:
func updateFromManagedObject2(animal: Animal) {
}
func updateFromManagedObject2(person: Person) {
}
You can't override that method because types in objective-c and swift are a bit different. the types have to line up exactly, or overriding won't work.
Both statements So the subclass method's parameters' types must be the same or more general than the parameters' types for the superclass method it overrides. and You cannot overload Objective-C functions based on the argument type, as Objective-C doesn't support this kind of overloading above are correct.
Another example of overriding illustrating this:
- (UIColor*)getDataPointColor:(int)index
{
return UIColor.whiteColor;
}
override func getDataPointColor(_ index: Int32) -> UIColor {
return UIColor.redColor
}
Note that override func getDataPointColor(_ index: Int) -> UIColor { won't work. (Not sure why, 64-bit code and all...)

super bound statically at compile time?

I want to create a class cluster with a base class and 2 subclasses. Creating an instance of the base class should return a subclass based on some conditions, but creating a subclass directly should create it. I wrote the following code in the base class:
+ (id)allocWithZone:(NSZone *)zone {
// prevent infinite recursion
if ([self isEqual:Base.class]) {
// if self is the base class, return a correct subclass
if (somecondition) {
return [SubclassA alloc];
}
return [SubclassB alloc];
}
// otherwise, alloc is called on a subclass
// call NSObject's alloc
return [super allocWithZone:zone];
}
and it works, but I'm really surprised that it does. Namely, when invoked on a subclass, why does super evaluate to the Base class's superclass (NSObject), and not the Base class (because invoked on SubclassA, the superclass is Base)? It is as if the allocWithZone: method call, inherited from Base, just always evaluated super relative to Base, not the real runtime class of the caller. I think similar code in Java and other OO languages would not work and result in infinite recursion, would it? Is this code wrong?
Your code is correct. [super ...] always uses the superclass of the class implementing the method. In your code, +allocWithZone: is implemented by class Base, so [super allocWithZone:zone] uses Base's superclass when searching for the next +allocWithZone: implementation to call.

what does the compiler when it finds [super msg];

I've read apple 'messaging' chapter from programming with objective - c and got several questions about self and super.
AFAIK when compiler finds any message it translates it into objc_msgSend with two hidden parameters - receiver, selector and variable arguments for selector.
for example [self test] will be something like that:
objc_msgSend(self, #selector(test));
if there is no method implementation in receiver's dispatch table then function will try to find implementation in superclasses.
super is just a flag for the compiler to start searching method implementation in the superclass of current object, and in documentation apple said that when compiler finds 'super' it translates it in something like that :
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, #selector(forwardedMethod));
I've made a project with 3 classes, each one inherits from another.
#interface FirstClass : NSObject
- (void)forwardMethod;
#end
#interface SecondClass : FirstClass
#end
#interface ThirdClass : SecondClass
#end
I created an instance of third class in my root view controller and invoke his method called 'forwardMethod'.
The implementation :
//First Class
- (void)forwardMethod {
NSLog(#"Base class reached");
}
//SecondClass imp
- (void)forwardMethod {
NSLog(#"second class");
[super forwardMethod];
}
//ThirdClass imp
- (void)forwardMethod {
NSLog(#"third class");
[super forwardMethod];
}
Everything works fine. But then i decided to interpret compiler:
//First Class
- (void)forwardMethod {
NSLog(#"Base class reached");
}
//SecondClass imp
- (void)forwardMethod {
NSLog(#"second class");
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, #selector(forwardMethod));
}
//ThirdClass imp
- (void)forwardMethod {
NSLog(#"third class");
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, #selector(forwardMethod));
}
Which results in an recursive call to the second class 'forwardMethod'. I create a struct in 'forwardMethod' at second class using self and [self superclass], but self is thirdclass and
my superclass will be always 'second class'. Maybe I'm doing something wrong, but how i can get to the base class 'forward method'?
Note: For educational purposes only (which is good!), don't use in production code!
You are also most there, just one class out...
To see why you get the recursion consider how the super class can be found using the information available at compile time and at run time.
At runtime the value of self is a reference to the current object, you can use self to find the class of the object - in your example self is an object of type ThirdClass.
Now the value of self doesn't change as methods are called, so in your example even in FirstClass's forwardMethod the value of self is a reference to an object of type ThirdClass. So self enables you to find the type of the object, but it doesn't tell you where you are currently executing a method in its inheritance chain, so by itself it can't tell you what the next class in that chain is.
So consider compile time. When compiling SecondClass the compiler knows it super class is FirstClass, so a call to super is a call to a method in FirstClass (apart from a caveat coming below). So the compiler can use self and [FirstClass class] to determine the runtime object on which to call the method and the compile time class at which to start the search for the method (as any method lookup is a search starting at a class and proceeding along the inheritance chain until and implementation is found). So in your example code you were just one method out:
#implementation SecondClass
- (void)forwardMethod
{
NSLog(#"second class");
struct objc_super mySuperClass =
{
self,
[FirstClass class]
};
objc_msgSendSuper(&mySuperClass, #selector(forwardMethod));
}
If you use that you code will work. But...
...that caveat mentioned above. Objective-C allows the inheritance chain to be altered at runtime using class swizzling, so the compiler cannot in general rely on the compile time inheritance chain to work out the super class. So what can it use? Well when compiling the source for a particular method it knows the class that method belongs to, so it could compile that methods class into the code to find the super class and use code which starts the search for a method at the next class along the runtime inheritance chain. This is in fact what the compiler will do for your code, just in case you've used class swizzling:
#implementation SecondClass
- (void)forwardMethod
{
NSLog(#"second class");
struct objc_super mySuperClass =
{
self,
[SecondClass class]
};
objc_msgSendSuper2(&mySuperClass, #selector(forwardMethod));
}
Note the compiler passes the compile time current class ([SecondClass class]) and calls objc_msgSendSuper2 to perform the lookup - which will find the first method in the runtime inheritance chain that is after SecondCLassm whereas `objc_msgSendSuper would start the search at SecondClass itself.
Have fun, but don't use this in general code (unless you have a very, very, very ... very good reason ;-))
In your SecondClass this structure will be populated with exactly the same contents as your ThirdClass:
struct objc_super mySuperClass = {
self,
[self superclass]
};
self has the same value in both cases and, thus, [self superclass] will always be SecondClass when called from an instance of ThirdClass (even when the actual implementation -- the code -- resides in SecondClass). There is a bit more magic to what the compiler emits (objc_msgSendSuper is quite straightforward) in that it has to emit a reference to the class such that even things like posing and/or isa pointer manipulation -- bad programmer, no donut -- still work as expected. I haven't looked into the details in long, long while to know exactly how that works.
The source to the runtime and the compiler is available.