I have an Objective-C++ file, and I have two classes: one Objective-C, one C++:
#implementation ClassA
....
// Create a copy of MyClass and use it in another C++ class
instanceOfCppClassB->callFunction(new MyClass);
#end
class MyClass : public AnotherClass
{
....
};
This compiles and runs fine with the C++ class up on top, but I'd like to move it to the bottom. When I move it to the bottom I get the error:
Invalid use of incomplete type 'struct MyClass'
Forward declaration of 'struct MyClass'
Regardless of using typedef,struct,#class I get no love. How do I forward declare this class?
Forward declaration of a C++ class does not allow you to use instances of the class, you can just pass them around. (To simplify the example, I have omitted any Objective-C.)
class Something;
void function(void)
{
Something *x; // Ok
x = new Something(); // Error
int z = x->field; // Error
x->method(); // Error
}
class Something : public Other { ... };
void function2(void)
{
Something *x; // Ok
x = new Something(); // Ok
int z = x->field; // Ok
x->method(); // Ok
}
You must put the entire definition of a class before you use it. The forward declaration only allows you to declare variables using the class's type.
So the answer is: what you ask is impossible. (What is wrong with putting the class definition at the top, anyway?)
You can still put methods at the bottom:
class Something {
public:
void method();
};
#implementation ...
...
#end
void Something::method() { ... }
Just add class MyClass Prototype before ClassA.
class MyClass;
....
#implementation ClassA
....
// Create a copy of MyClass and use it in another C++ class
instanceOfCppClassB->callFunction(new MyClass);
#end
Related
I hope the title is precise enough.
I was wondering, how I can pass a interface implementation to an object in objc language.
In java it would look like:
public interface MyInterface {
void onData();
}
The implementing class
public class ImplementMyInterface {
// ...
// other methods
///
void registerInterface(){
MyInterface myInterface = new MyInterface(){
#Override
public void onData(){
// process data here within the class
}
};
}
}
And in objc?
id myinterface.
How to implement it in the class?
Is there only the possibility to let the class inherit the interface?
Like
interface MyInterface : NSObject
and the implementing class
MyImplementingClass : MyInterface
Or is there another possibility?
Thank you in advance
Objective-C has anonymous functions (blocks), but it doesn't have anonymous classes. So, the only way to implement a protocol (which is the objective-c term for an interface) is to make some class conform to that protocol (using your terminology, make that class "inherit" from that protocol) and add a protocol implementation inside that class' implementation.
I was able to solve my problem.
I was only able to import the MyInterface header file in my ImplementMyInterface.m file, but rather in the ImplementMyInterface.h file.
So everything I could do was inside the ImplementMyInterface.m file.
// ImplementMyInterface.m
#import "MyInterface.h"
// inner class
#interface MyInternalInterface : NSObject<MyInterface>
#property (retain) ImplementMyInterface * implementation;
#end
// the actual class
#implementation ImplementMyInterface
MyInternalInterface * _internalInterface;
+(instancetype) build {
// construct myself
ImplementMyInterface * implementatMyInterface = [[ImplementMyInterface alloc] init];
// init inner class object
_internalInterface = [[MyInternalInterface alloc] init];
// register myself
[_internalInterface setImplementation:implementatMyInterface];
return implementatMyInterface;
}
- (NSString *) theActualData {
return #"The actual data";
}
// end of implementation class
#end
// implementation of inner class
#implementation MyInternalInterface
#synthesize implementation;
- (NSString *) onData {
if(implementation != nil)
return [implementation theActualData];
return #"";
}
// end of ImplementMyInterface.m
In Objective-C, NSObject had a class method called load that gets called when the class is loaded for the first time. What is the equivalent in Swift?
#implementation MyClass
+ (void)load
{
[self registerClass];
}
#end
Prior to Swift 1.2:
override class func load() {
NSLog("load");
}
EDIT:
As of Swift 1.2 you can no longer override the load method. Look into the method initialize instead, it behaves different than load though, it get's called the first time the class is being referenced somewhere rather than on application initial load
Support for overriding load was removed in Swift 1.2
Update: Starting from Swift 5 class extensions and categories on Swift classes are not allowed to have +load methods, +initialize doesn’t seem to be prohibited, though.
While direct equivalent is not available with Swift, this can be achieved relatively elegantly with Objective-C and categories. Foo should still inherit from NSObject and have a class / static non-private swiftyLoad/Initialize methods, which get invoked from Objective-C in Loader.m, which you include in compile sources alongside Foo.swift:
# Loader.m
#import <Foundation/Foundation.h>
#import <MyProject/MyProject-Swift.h>
// This was a more concise solution in Swift 4.2 and earlier. If you try
// this with Swift 5 you'd get "Swift class extensions and categories
// on Swift classes are not allowed to have +load methods" runtime error.
// #implementation Foo (private)
// + (void)load { [self swiftyLoad]; }
// + (void)initialize { [self swiftyInitialize]; }
// #end
// This is what you'd want to use with Swift 5 onwards, basically
// just a pure Objective-C defined class.
#interface Loader : NSObject
#end
#implementation Loader : NSObject
+ (void)load { [Foo swiftyLoad]; }
+ (void)initialize { [Foo swiftyInitialize]; }
#end
# Foo.swift
import Foundation
public class Foo: NSObject {
#objc public static func swiftyLoad() {
Swift.print("Rock 'n' roll!")
}
#objc public static func swiftyInitialize() {
Swift.print("Hello initialize!")
}
}
The best part there's no need to mess with bridging headers or anything else, it just works. There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
in Swift 2.0, Please use this method
public override class func initialize()
What is the equivalent in Swift?
There is none.
Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.
Source: "The Swift Programming Language", by Apple
Types simply don't have an initializer in Swift. As several answers here suggest, you may be able to work around that by using the Objective-C bridge and having your Swift class inherit from NSObject or by using a Objective-C loader bootstrap code but if you have a 100% Swift project, you basically only have two options:
Manually initialize your classes somewhere within the code:
class MyCoolClass {
static func initClass ( ) {
// Do your init stuff here...
}
}
// Somewhere in a method/function that is guaranteed to be called
// on app startup or at some other relevant event:
MyCoolClass.initClass()
Use a run once pattern in all methods that require an initialized class:
class MyCoolClass {
private static var classInitialized: Bool = {
// Do your init stuff here...
// This code will for sure only run once!
return true
}()
private static func ensureClassIsInitialized ( ) {
_ = self.classInitialized
}
func whateverA ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverB ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverC ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
}
This follows a clean pattern that no code in Swift runs "automagically", code only runs if other code instructs it to run, which provides a clear and trackable code flow.
There might be a better solution for special cases yet you have not provided any information as for why you need that feature to begin with.
The abilitity to use this method was removed in this PR.
It provides some rationale:
Swift's language model doesn't guarantee that type metadata will ever really be used, which makes overriding initialize() error-prone and not really any better than manually invoking an initialization function. Warn about this for Swift 3 compatibility and reject attempts to override +initialize in Swift 4.
For Swift 2 or 3 (i.e. post-Swift 1.2), you can use:
class MySwiftClass: NSObject {
internal override class func initialize() {
DoStuff()
super.initialize()
}
}
But, as you can see, your class needs to inherit (directly or indirectly) form NSObject. This is required because the initialize() is called by the ObjC runtime.
And the initialize() method will only be called when MySwiftClass is referenced. So it will not be as magic as load().
But it will also be safer. For example: including a framework (let's say, by just adding it to your Podfile) won't allow the framework to mysteriously start to behave as soon as your app launches, without the need to add a single line of code to your project (at least… I hope! 😉).
I get conclusion for swift 1.2~5 doable ways:
Doable |swift-load |swift-initialze|bridgeToOC-load|bridgeToOC-initialze|
--- |---- |--- |--- |--- |
swift1.2~4.2|X |O |O |O |
swift4.2~5.0|X |X |O |O |
swift5.0~? |X |X |X |O |
and how to make it ? check here
https://medium.com/post-mortem/using-nsobjects-load-and-initialize-from-swift-f6f2c6d9aad0
but I think here is another way to make it swifter. use runtime and protol without load/initialze.
http://jordansmith.io/handling-the-deprecation-of-initialize/
Swift 5
Method 'load()' defines Objective-C class method 'load', which is not permitted by Swift
You can not override load() method now but there is a legal way to call your custom swift_load() method of your swift classes on app startup. It is needed to make ObjC class which redirect its own load() method to your swift classes.
Just add next SwiftLoader.m file to your project:
// SwiftLoader.m
#interface SwiftLoader : NSObject
#end
#implementation SwiftLoader
+ (void)load {
SEL selector = #selector(swift_load);
int numClasses = objc_getClassList(NULL, 0);
Class* classes = (Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class class = classes[i];
Method method = class_getClassMethod(class, selector);
if (method != NULL) {
IMP imp = method_getImplementation(method);
((id (*)(Class, SEL))imp)(class, selector);
}
}
free(classes);
}
Now you can add swift_load class method to any of your swift classes to make setup tasks:
class MyClass {
#objc
class func swift_load() {
print("load")
}
}
What I'm trying to do is quite simple but I probably have the syntax wrong.
I have an Objective-C Interface with a Note class parameter. Note.h is a C++ class that basically looks like this:
#include <string>
using namespace std;
class Note {
public:
string name;
Note(string name){
this->name = name; // ERROR: Cannot find interface declaration for 'Note'
}
};
This is my controller where is using Note. I changed the file extension to .mm
#class Note;
#interface InstrumentGridViewController : UIViewController {
#public
Note* note;
}
#property (nonatomic, retain) Note* note;
And this is how I'm using it:
#implementation InstrumentGridViewController
#synthesize note;
- (void)buttonPressed:(id)sender {
note = Note("fa"); // ERROR: Cannot convert 'Note' to 'Note*' in assignment
NSLog(#"naam van de noot is %s", note->name); // ERROR: Cannot find interface declaration for 'Note'
}
I'm getting these three errors though (I've added them as comments on the right lines)
Any idea how what I'm doing wrong?
You need to allocate the Note object using new:
- (void)buttonPressed:(id)sender
{
if (note != 0)
delete note;
note = new Note("fa");
NSLog(#"naam van de noot is %s", note->name.c_str());
}
However it doesn't seem right to be doing that in the button press action method...
Also don't forget to delete it in your object's dealloc method:
- (void)dealloc
{
delete note;
[super dealloc];
}
Lastly your #property attribute retain is wrong as it's not an Objective-C object; instead use assign and better still make it readonly.
A better way to initialise most objects is by using a const reference to them rather than a copy:
Note(const string &name)
{
this->name = name;
}
Your Note C++ class is not valid. Change its declaration to be instead:
class Note {
public:
string name;
Note(string aName) {
name = aName;
}
};
Also change your InstrumentGridViewController:
- (void)buttonPressed:(id)sender {
note = new Note("fa");
NSLog(#"naam van de noot is %s", note->name);
}
- (void)dealloc {
delete note;
[super dealloc]; // Use this only if not using ARC
}
The Cannot find interface declaration for 'Note' error was caused because of #class Note in my Obj-C controller .h file. It's strange because I have a working sample project where #class is used and it works fine.
I fixed it using forward declarations the way they are described here and here.
// used typedef struct <classname> <classname>
typedef struct Note Note;
// instead of
#class Note
The above goes in the Obj-C header file. In the .mm file goes the #import "Note.h" statement
Of Objective C, I'm looking to call a variable from one .m to the other .m
This is given myvar declared as an int in Example1.h
Example1.m
myvar = myvar+10
Example2.m
if (myvar == 10){NSLOG("#myvar equals the correct integer: %i",myvar);}
However, by default myvar will equal 0 because myvar is called from Example1.h in Example2.m.
For global values, create a class to hold these and define the variables as static. You can also define class level methods to manipulate the static variable. I call my class appState. You might define myVar as static and then class methods (use the + not -) to get and set this variable.
Here's an example of a BOOL I can access from anywhere in my application.
account.h
#import <Foundation/Foundation.h>
#interface Account : NSObject
{
}
+(BOOL)isOffLine;
+(void)setOffLine:(BOOL)newValue;
#end
account.m
#import "Account.h"
#implementation Account
static BOOL _offline;
+(BOOL)isOffLine;
{
return _offline;
}
+(void)setOffLine:(BOOL)newValue
{
_offline = newValue;
}
#end
Now from any class in my application, I can #import account.h and then use something like:
if ([Account isOffLine]) {...}
or
[Account setOffLine:YES];
Note that I didn't create an instance of this class. I'm calling the class level methods. The value will persist between calls from different classes in my application.
I’d recommend you read up on the basics, perhaps Object-Oriented Programming with Objective-C could be a good place to start. My guess is that what you really should be doing is creating a property in one class and accessing it from another.
I've read many things about enum types in objective-c, and I see there is many ways to define them. But I don't see the right way (if there is one) to define an enum that can be called with CARS.ROLLSROYCE and that cannot be used only with ROLLSROYCE in the code.
So I can define ROLLSROYCE in the CARS enum and also in the BEAUTIFULCARS enum.
Do you know the way to define such an enum ?
You are trying to implement namespaces for your Enums in Objective-C. What you're asking for is a lot of elbow grease in Objective-C. You are probably best-off using C++ for this, since it is easy and afaik fully supported in any iOS or Cocoa application. You'll have to rename the files that #import your C++ code to .mm files instead of .m files, and the C++ compiler can be trickier than the Objective-C one. Going this route you'll create a header file like Enums.h.
// Enums.h
namespace CARS
{
enum CARS
{
ROLLSROYCE
};
}
namespace BEAUTIFULCARS
{
enum BEAUTIFULCARS
{
ROLLSROYCE = 45
};
}
And in your .mm sourcefile
#import "Enums.h"
-(void)printEnumvals
{
NSLog(#"CARS %d BEAUTIFULCARS %d",
CARS::ROLLSROYCE,
BEAUTIFULCARS::ROLLSROYCE);
}
If you want to avoid using C++ for this solution, there's a lot more elbow grease, bookkeeping, and opportunity for error. You'll need a header and a source file for this.
// CARS.h
#interface BEAUTIFULCARS : NSObject
{
enum
{
BEAUTIFULCARS_ROLLSROYCE = 45
} BEAUTIFULCARS;
}
#end
#interface CARS : NSObject
{
enum
{
CARS_ROLLSROYCE
} CARS;
}
#end
// CARS.m
#implementation BEAUTIFULCARS
+(NSInteger)ROLLSROYCE{ return BEAUTIFULCARS_ROLLSROYCE; }
#end
#implementation CARS
+(NSInteger)ROLLSROYCE{ return CARS_ROLLSROYCE; }
#end
Your .m source is almost the same:
#import "CARS.h"
-(void)printEnumvals
{
NSLog(#"CARS %d BEAUTIFULCARS %d",
CARS.ROLLSROYCE,
BEAUTIFULCARS.ROLLSROYCE);
}
Objective-C does not manage scope in the same way that most other OO languages do. Interfaces define the properties and messages that an object that interface supports, but don't support protection levels like public or private. When you define an enum in an #interface, that enum ends up in global scope.
For my case, I didn't want to use C++ namespaces or write redundant Objective-C classes for such a simple case, so I fallen back to the C.
// Enum.h
typedef struct
{
const int ROLLSROYCE;
} _CARS;
typedef struct
{
const int ROLLSROYCE;
} _BEAUTIFULCARS;
extern const _CARS CARS;
extern const _BEAUTIFULCARS BEAUTIFULCARS;
And then in Enum.m, define values
// Enum.m
#import "Enum.h"
const _CARS CARS = {0};// 0 is to be assigned to ROLLSROYCE field in struct
const _BEAUTIFULCARS BEAUTIFULCARS = {1}; // same but with 1
And finally, in your "main" code
#import "Enum.h"
// Some method
{
NSLog(#"I can refer to CARS.ROLLSROYCE = %d and BEAUTIFULCARS.ROLLSROYCE = %d", CARS.ROLLSROYCE, BEAUTIFULCARS.ROLLSROYCE);
}
Which will produce this output:
I can refer to CARS.ROLLSROYCE = 0 and BEAUTIFULCARS.ROLLSROYCE = 1