How do you initialise a constant in a header file?
For example:
#interface MyClass : NSObject {
const int foo;
}
#implementation MyClass
-(id)init:{?????;}
For "public" constants, you declare it as extern in your header file (.h) and initialize it in your implementation file (.m).
// File.h
extern int const foo;
then
// File.m
int const foo = 42;
Consider using enum if it's not just one, but multiple constants belonging together
Objective C classes do not support constants as members. You can't create a constant the way you want.
The closest way to declare a constant associated with a class is to define a class method that returns it. You can also use extern to access constants directly. Both are demonstrated below:
// header
extern const int MY_CONSTANT;
#interface Foo
{
}
+(int) fooConstant;
#end
// implementation
const int MY_CONSTANT = 23;
static const int FOO_CONST = 34;
#implementation Foo
+(int) fooConstant
{
return FOO_CONST; // You could also return 34 directly with no static constant
}
#end
An advantage of the class method version is that it can be extended to provide constant objects quite easily. You can use extern objects, nut you have to initialise them in an initialize method (unless they are strings). So you will often see the following pattern:
// header
#interface Foo
{
}
+(Foo*) fooConstant;
#end
// implementation
#implementation Foo
+(Foo*) fooConstant
{
static Foo* theConstant = nil;
if (theConstant == nil)
{
theConstant = [[Foo alloc] initWithStuff];
}
return theConstant;
}
#end
A simple way for value type constants like integers is to use the enum hack as hinted by unbeli.
// File.h
enum {
SKFoo = 1,
SKBar = 42,
};
One advantage to this over using extern is that it's all resolved at compile time so there no memory is needed to hold the variables.
Another method is to use static const which is what was to replace the enum hack in C/C++.
// File.h
static const int SKFoo = 1;
static const int SKBar = 42;
A quick scan through Apple's headers shows that the enum hack method appears to be the preferred way of doing this in Objective-C and I actually find it cleaner and use it myself.
Also, if you are creating groups of options you should consider using NS_ENUM to create a typesafe constants.
// File.h
typedef NS_ENUM(NSInteger, SKContants) {
SKFoo = 1,
SKBar = 42,
};
More info on NS_ENUM and it's cousin NS_OPTIONS is available at NSHipster.
Related
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'm trying to use the PImpl idiom to use the <vector> libray in my Objective-C project.
I have managed to implement it, but it only for the Int type as you can see in my .h and .mm file.
file.h
#import <Foundation/Foundation.h>
struct Vector_impl;
#interface MM_Matrix : NSObject
{
struct Vector_impl *MMVec;
}
#property unsigned rows, columns;
- (MM_Matrix *) initwith:(int) n and: (int) m;
- (long) size: (int) n;
file.mm
#import "MM_Matrix.h"
#include <vector>
struct Vector_impl
{
std::vector<std::vector<int>> matrix;
};
#implementation MM_Matrix : NSObject
- (MM_Matrix *) initwith:(int) n and:(int)m
{
[self setRows:n];
[self setColumns:m];
MMVec = new Vector_impl;
MMVec->matrix.resize(n);
for (int i=0; i<n; i++) {
MMVec->matrix[i].resize(m, 0);
}
return self;
}
- (long) size: (int) n
{
return MMVec->matrix[n].size();
}
#end
I would like to implement a generic type (template maybe?) as you would do in the <vector> library, but I have no idea how to achieve that.
Assuming that you want to template std::vector<std::vector<int>>: there are two possibilities:
Use a weak type, e.g. void* and take care of conversion inside the class.
Implement an interface for every type you want to support.
For the second option it is enough to instantiate the template class for every type, so that the compiler knows which types are used.
Also have a look at this related question: pimpl for a templated class.
This question already has answers here:
Objective-C Static Class Level variables
(9 answers)
Closed 8 years ago.
Does someone know how to pass static object in objective c ?
In java its something like :
class A {
static int x;
...
}
class B {
...
A.x = 4;
}
something alike.
Someone know how to achieve the same result using Objective C NSString ?
thanks.
In Objective-C, there are no class (static) variables. One thing you can do is to use global variables, but that's generally discouraged:
// A.h
extern int x;
// A.m
int x;
// B.m
#import "A.h"
x = 4;
However, you should instead rethink your code design, you should be able to get away without using globals.
You'll have to declare your variable on the top of your .m and create a getter and setter for the static variable and work with that
static int x;
+ (int)getX {
return x;
}
+ (void)setX:(int)newX {
x = newX;
}
Objective-C doesn't have static/class variables (note that the difference between static and class methods is subtle but significant).
Instead you can create accessors on the class object and use a global static to store the value:
#interface MyClass : NSObject
+(NSString *)thing;
+(void)setThing:(NSString *)aThing;
#end
#implementation MyClass
//static ivars can be placed inside the #implementation or outside it.
static NSString *_class_thing = nil;
+(void)setThing:(NSString *)aThing {
_class_thing = [aThing copy];
}
+(NSString *)thing {
return _class_thing;
}
//...
#end
There is no direct way to do in Obj-C.
You need to create a class-method, that will access the static property.
// class.h
#interface Foo {
}
+(NSString *) string;
// class.m
+(NSString *) string
{
static NSString *string = nil;
if (string == nil)
{
// do your stuff
}
return string;
}
I am trying to create a class with static const variables that can be used from outside the class, but I cannot figure out how to initialize this variable.
Example Code:
#interface ExampleClass
{
static const int CONST_VAR;
}
- (id) init;
#end
#implementation ExampleClass
- (id) init {
CONST_VAR = 1;
}
#end
I want to be able to reference the static constant variable like this:
ExampleClass.CONST_VAR;
You should assign a value to this static variable by doing the following:
-(id)init{
ExampleClass.CONST_VAR = 1;
}
Because this is a static variable or "class variable", you must use the class name in any case regardless of where you are this includes from inside the same class.
Hope this helps.
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