Objective-C #implementation variables visibility - objective-c

I got two classes, which implementation contains an array named "geometryCoords", in "Corridor" class it is initialized in declaration, and in "RhombusArray" it is done in the "init" method:
GLfloat _geometryCoords[]={/*Values here*/};
GLfloat _geometryCoords[];
...//In the init method
_geometryCoords = (GLfloat*)malloc(somevalue);
for(...){/*Assignment*/}
While debugging this method i noticed that "geometryCoords" has the values of the first class' array before the initialization code is passed. After changing the declaration of array in "RhombusArray" from:
GLfloat _geometryCoords[];
to
GLfloat* _geometryCoords;
i got an "Apple Mach-o linker error: duplicate symbol _geometryCoords in /BlaBla/Corridor.o and /BlaBla/RhombusArray.o".
Both of those classes' headers are imported to the .mm file and removing one of them solves the conflict.
How should i handle this problem?

Both of these arrays must have global scope (i.e. they are declared outside of a function and outside of a class interface) for you to get the linker error and the other issue. Declare them like this:
static GLfloat _geometryCoords[]={/*Values here*/};
//^^^^ this is the new bit
The static keyword makes the symbol visible only within the compilation unit (the .m file) in which they are declared.

Related

Objective-C float #define not accessible in Swift4

I am migrating code from Objective-C to Swift 4.0. Here I have some float #define constants related to my deviceHeight in Specific Objective-C header class. While accessing this #define giving error "Use of unresolved identifier". When I use Objective-C string #define identifier it's easily accessible within Swift class.
Not accessible in Swift4
#define PHONE_IPHONE10 PHONE_UISCREEN_HEIGHT==812.0f
Accessible in Swift4
#define ERROR #"Some error occured. Please try later."
Help me with your comments or solution.
The reason this imports to Swift...
#define ERROR #"Some error occured. Please try later."
...is that it’s semantically equivalent to a constant declaration. That is, it permanently associates that string-literal value with the name ERROR. The Swift compiler recognizes that you’re using the C preprocessor to define a constant, and translates it to a Swift constant.
(Even though you could—and probably should—define C global constants without the preprocessor, Swift recognizes that there’s a long tradition of using #define instead, and imports it anyway.)
The reason this doesn’t import to Swift...
#define PHONE_IPHONE10 PHONE_UISCREEN_HEIGHT==812.0f
...is that this is a preprocessor macro. It doesn’t statically map a name to a value. Instead, it tells C that wherever it sees your name PHONE_IPHONE10, it should substitute the expression PHONE_UISCREEN_HEIGHT==812.0f. Presumably PHONE_UISCREEN_HEIGHT is itself a macro, so the whole thing expands to a chain of method calls and an equality comparison.
Swift itself doesn’t do preprocessor macros, or anything like such, so it doesn’t import them from C.
A close equivalent would be to redefine this logic using a computed property or function (and the idiomatic way to do that in Swift would be as a static member on a type, not a global symbol). Something like this:
extension UIDevice {
class var isMaybeiPhoneX: Bool {
return false // or some logic based on UIScreen.main.size
}
}
But be warned, the whole idea of conditionally changing your app’s UI or behavior based on a specific screen height check is fraught with peril. Tried Auto Layout?
To achieve similar functionality I created Constants.swift file with this structure:
struct Constants {
struct phoneHeights {
static let PHONE_UISCREEN_HEIGHT = 812.0
//some others consts
}
struct iPhoneX {
static let statusBarHeight: CGFloat = 44
//some others consts
}
}
Or simply:
struct Constants {
static let PHONE_UISCREEN_HEIGHT = 812.0
static let statusBarHeight: CGFloat = 44
}
And for type safety in Swift, you can read here.

Swift class properties not initialized when constructed by Objective C code

I'm attempting to create a class in Swift 3 to implement a Cordova plugin. I have this building and running, but the application crashes whenever any properties of the class are accessed. I've tried two ways of initializing the class:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players = [UUID:DSFPlayerHandler] ();
...
}
and
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler];
override init () {
players = [:];
}
...
}
However, when my players property is used, the result is a EXC_BAD_ACCESS exception, with an address that looks like a null pointer dereference.
The object is being created by Objective C code, which is a language I have no familiarity with at all, but I think this is the line that creates it:
obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
The CDVPlugin class contains a comment stating that initWithWebViewEngine should not be overridden (and indeed I do not seem to be able to override this method, because while it is declared in the CDVPlugin.m file, it isn't mentioned in CDVPlugin.h, so the Swift compiler doesn't seem to know about it), but rather initialization code should be placed in a method called pluginInitialize instead. However, if I do that I get a compiler error ("Class DSFMediaCentre has no initializers").
Furthermore, if I put my init() method back in and set it to call pluginInitialize(), like this:
override init () {
super.init(); // necessary otherwise next line is an error
pluginInitialize();
}
override func pluginInitialize() {
players = [:];
}
the error then changes to "Property 'self.players' not initialized at super.init call".
How do I make this class initialize correctly?
You have a mismatch between the strict initialization system required by the language and the procedure used by the framework you're working with.
Swift demands that a) properties be initialized as part of object construction, and b) that construction be chained to the type's supertype. But the CDVPlugin type is doing the construction on your behalf; you don't have the ability to customize it. (This makes more sense in ObjC, because it doesn't have the same compile-time restrictions as Swift.)
The situation is similar to unpacking an object from a nib file. In that case too, because it's the nib loading system that's constructing your object, you don't have the ability to customize the initializer. Your type will always be constructed by init(coder:). In a certain sense, your initialization point moves further down, to awakeFromNib(), and among other things, that forces outlets to other objects in the archive to be declared as optional, usually implicitly unwrapped.
The same solution should avail you here. You should consider pluginInitialize() to be your initialization point. The language then requires that properties be optional, since they are not filled at its initialization point. Therefore, make the property an IUO:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler]!
override func pluginInitialize() {
players = [:];
}
}
and all should be well.
The other solution is to use lazy keyword
lazy var players :[UUID:DSFPlayerHandler] = [:]
So, you don't need to initialize players in initializer but still make sure players always non-nulable

Swift does not generates forward declaration for protocol

I have a swift class defined in a framework that is being used from an obj-c app.
The generated -Swift.h header contains the swift classes marked with #objc but there's one property that makes the compilation fail.
This property is defined like this in swift code :
public var storageClass : StorageProtocol.Type = UserDefaultStorage.self
and so the generated obj-c property looks like this
#property (nonatomic) Class <StorageProtocol> __nonnull storageClass;
But Xcode does not accepts the "StorageProtocol" symbol here, because the forward declaration "#protocol StorageProtocol;" is missing.
If I add a new var defined like this :
public var storage : StorageProtocol? = nil
The forward declaration is added on top of class that define these properties and the -Swift.h compilation succeed.
So it looks like a bug in the -Swift.h generation, but is there another way to force that forward declaration without using a dummy var ?
I did not find any other way than using a dummy var...

Weak Link My Own Objective-C Class

Is it possible to weak link my own objective-c classes?
I have seen that I can weak link a function or a variable…
extern int MyFunction() __attribute__((weak_import));
extern int MyVariable __attribute__((weak_import));
I would like to have something like this…
if ([MyUploadManager class]) {
self.uploadButton.hidden = NO;
}
… and be able to compile even if UploadManager.m is not included in the project.
To weak link a class e.g. MyUploadManager in your own executable:
To keep the linker happy, add this to Other Linker Flags in the project:
-Wl,-U,_OBJC_CLASS_$_MyUploadManager
This allows the class symbol to be undefined even if it is not built into your executable. It will be considered for dynamic lookup instead, effectively the same as a dynamic library symbol.
To keep the runtime happy, add this to your class header:
__attribute__((weak_import)) #interface MyUploadManager
When the dynamic linker runs, it substitutes a nil for the class symbol rather than crashing.
Now you can run this without either linker or runtime errors:
if ([MyUploadManager class]) {
self.uploadButton.hidden = NO;
}
Note: As of Xcode 7, the -U linker options conflicts with BitCode, so you may not be able to use this technique for future projects.
You can use the NSClassFromString function:
Class MyUploadManager = NSClassFromString(#"MyUploadManager");
if (MyUploadManager) {
self.uploadButton.hidden = NO;
}
NSClassFromString returns nil if the class cannot be found.
To weak link a class it can be included in a framework. The compiler can be told to weak link all symbols in a framework using the Other Linker Flag build setting.
-weak_framework <framework_name>
This enables MyModule.framework to weak link against Uploader.framework whilst it is being built. If someone using MyModule.framework does not links against Uploader.framework then, in the example above, the button will not be displayed.
Frameworks and Weak Linking

class variable scope issue with objc / cocoa?

Compiling in XCode 3.1.3 using GCC 4, under Leopard 10.5.8, with 10.5 as my target...
I have an interface, thus:
#interface testThing : NSObject { classContaininghooHa *ttv; }
#end
And an implementation, thus:
#implementation: testThing
- (void) instanceMethodMine
{
[ttv hooHa]; // works perfectly, compiles, links, hooHa is invoked.
}
// void cFunctionMine()
// {
// [ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function) "
// }
void stupidCFunctionMine((testThing *)whom) // whom is passed class 'self' when invoked
{
[whom instanceMethodMine]; // compiles, links, works. :/
}
#end
Now, my understanding -- clearly flawed -- was that if you declared a variable, class ID or otherwise, it was private to the class, but within the class, is performed essentially as a global, stored in the allocated class instance for the duration of its existence.
That's how it acts for objc methods.
But in the c function above, also written within the class, the variable appears to be invisible. The doesn't make sense to me, but there it is.
Can someone explain to me what is going on?
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
Insight much appreciated.
It doesn't make any difference where you are declaring/defining your "normal" c functions. They are not part of the class, they are just plain old c functions. No connection to the class whatsoever. Passing the instance they work on is a workaround if you really don't want to make this function a true objective-c method.
interface methods have full access to it's member variables. And the C function is not part of the class and so it cannot access any class variables unless it takes an class instance as the argument.
void cFunctionMine()
{
[ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function)
}
Clearly cFunctionMine is not part of the interface. So it does not what ttv is to send the message hooHa.
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
void cFunctionMine()
{
// 1. Create an instance using alloc and init
testThing *ttv = [ [testThing alloc] init ] ;
[ttv hooHa] ;
// Now the above statement is valid. We have a valid instance to which
// message can be passed to.
// .....
[ ttv release ] ;
// release the resources once you are done to prevent memory leaks.
}