I am developing a command line app which uses obj-c and c files together. In my obj-c file (say x.m), I use a struct which uses an interface and the interface uses a struct. This is easily handled in C++ with forward declarations but I need obj-c in my app.
I was wondering if someone can please shed some light on what I doing wrong.
Appreciate any help and thanks in advance.
typedef struct mystruct_s
{
...
....
} mystruct;
struct abc ;
#interface abcDelegate:NSObject {
#public
struct abc *abc;
}
#end
struct abc
{
mystruct b
abcDelegate *abcdelegate;
};
I get the following error error:
expected specifier-qualifier-list before ‘mystruct’
You've forgot ;
should be
struct abc
{
mystruct b;
abcDelegate *abcdelegate;
};
If using .m file you must use c-style structs. such as
typedef struct mystruct_
{
...
} mystruct;
or
struct abc
{
struct mystruct b;
abcDelegate *abcdelegate;
};
If you want structs just like in c++ change your file extension to .mm to support c++
Almost there. Change the code to look like this:
struct abc
{
mystruct b;
abcDelegate *abcdelegate;
};
#interface abcDelegate:NSObject {
#public
struct abc *abc;
}
#end
Related
I upgraded an Objective-C project with some C underpinnings to Xcode 9, which is generating a new warning:
possible misuse of comma operator here [-Wcomma]
I'm actually not sure what this statement is doing, so I'm not sure how to handle the warning. What is the effect of this statement?
#interface MyClass : NSObject {
struct CStruct *myStruct;
}
...
#end
#implementation MyClass
- (void)myMethod {
delete myStruct, myStruct = 0;
}
#end
I can tell that the intent is to zero out the contents of the struct and then nil out the pointer to it, but what does that accomplish that the line below doesn't?
delete myStruct; myStruct = 0;
I have a function written in Swift that I want to call from Rust. I've tried exposing it through Objective-C, however I continue to get errors from ld saying it can't find _foo. The Rust project is linked to the Swift project by compiling the Rust project as a staticlib.
foo.h
#pragma once
#include <stdint.h>
uint8_t foo_bridge(uint8_t);
foo.m
#import <Foundation/Foundation.h>
#import <Foo-Swift.h>
uint8_t foo_bridge(uint8_t byte) {
return foo(byte);
}
bar.swift
public func foo(byte: UInt8) -> UInt8 {
return byte * 2
}
src/lib.rs
extern "C" {
pub fn foo_bridge(byte: u8) -> u8;
}
Bar-Bridging-Header.h
#import "foo.h"
Cargo.toml
[package]
name = "foo"
version = "0.1.0"
[lib]
name = "foo"
crate-type = ["staticlib"]
The issue here is attempting to call a bare Swift function from Objective-C, which isn't supported.
If you check the Foo-Swift.h header you'll find that the foo() function isn't present, which indicates that the symbol for it, _foo, won't be available at runtime either.
The solution is to put the foo() function inside something that can be called from Objective-C, like a class deriving from NSObject:
class Foo: NSObject {
class func foo(byte: UInt8) -> UInt8 {
return byte * 2
}
}
Once you do that, you'll find the Foo-Swift.h header now has an interface for it:
#interface Foo : NSObject
+ (uint8_t)fooWithByte:(uint8_t)byte;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Since we know it's available from Objective-C now, we can call it like this:
uint8_t foo_bridge(uint8_t byte) {
return [Foo fooWithByte:byte];
}
I have an enum defined in Objective C and used as a datatype in a Swift object.
Now I'm using that swift object in Objective C and I'd like to access the property.
However the property isn't added to the "xxxx-Swift.h" file in the class. I know it's possible to use Swift enums in Obj C with #objc prepending it but I don't have much control over this enum since it's auto generated from existing Objective C code.
I'm not sure exactly what your use case is, but here is a quick example of how an enum defined in Objective-C can be used in a Swift class, which can in turn be used in Objective-C.
Here is the Objective-C header (oclib.h):
#ifndef oclib_h
#define oclib_h
#import <Foundation/Foundation.h>
typedef enum {A, B, C} oc_enum_t;
void useSwiftClassInC();
#endif /* oclib_h */
Here is the corresponding Objective-C .m file (oclib.m):
#import "oclib.h"
#import "swift_c_1-Swift.h" // Need this to have access to Swift types; the swift_c_1 part will be your product module name.
void useSwiftClassInC() {
UseEnum *ue = [[UseEnum alloc] initWithE:B i:444];
printf("In Objective-C useSwiftClassInC(): m_Enum = %d, m_Int = %d\n", ue.m_Enum, ue.m_Int);
ue.m_Enum = A;
ue.m_Int = 10;
[UseEnum printUseEnum: ue];
}
And here is the Swift file:
// Swift class that uses an enum from Objective-C
class UseEnum : NSObject // NSObject inheritance is important!
{
var m_Enum : oc_enum_t
var m_Int : Int32
init(e : oc_enum_t, i : Int32)
{
m_Enum = e
m_Int = i
}
static func printUseEnum( x : UseEnum )
{
print("In Swift UseEnum::printUserEnum: m_Enum = \(x.m_Enum), m_Int = \(x.m_Int)")
}
}
// This calls an Objective-C function that does the following:
// - creates a UseEnum object
// - prints it out from Objective-C
// - modifies it
// - calls printUseEnum(), implemented in Swift, to print the object
useSwiftClassInC()
The bridging header just has
#import "oclib.h"
You have probably seen this documentation already, but if not, here it is: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Please provide more details about your specific situation if this doesn't answer your question. Here is the output I get from the example:
In Objective-C useSwiftClassInC(): m_Enum = 1, m_Int = 444
In Swift UseEnum::printUserEnum: m_Enum = oc_enum_t(rawValue: 0), m_Int = 10
An interesting situation arises if a data type defined in Swift is used in the signature of an Objective-C function that needs to be called from Swift. Xcode won't let us import the *-Swift.h into an Objective-C header or into a bridging header. A solution is to use #class forward declaration of the Swift type. Thus, if in the above example we needed an Objective-C function that takes UseEnum as a parameter, we could have something like this in oclib.h:
...
#class UseEnum; // Forward declaration must come before UseEnum is used!
...
void useSwiftClassInC_1(UseEnum * useEnum);
...
I'm really confused with this problem. What I need to do is use some obj-c runtime feature in my project. Here is simple code in my .m file:
#import "Base.h"
#import <objc/runtime.h>
#implementation Base
- (void)someMethod {
NSUInteger numberMethods = 0;
Method *classMethods = class_copyMethodList([self class], &numberMethods);
for (int i = 0; i < numberMethods; i ++) {
classMethods[i]->method_name; //incomplete definition of type "struct objc_method"
}
#end
I got the following error: incomplete definition of type "struct objc_method". After some inspecting objc/runtime.h file I found something like this:
some code...
typedef struct objc_method *Method;
...some code...
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
Is this something like forward declaration problem, or something else?
In addition to Martin answer, you should use functions like method_getName to retrieve the name of the method classMethods[i].
This is much more portable (especially these fields in the structure no longer exists since Objective-C 2.0 as the macro suggests) and will avoid problems like the one you have when the Runtime evolves.
These members cannot be accessed!
OBJC2_UNAVAILABLE macro indicates that a member is not available and only provides some insight into the structure.
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