I have a class structure
Class A
{
Object of classB
Object of classC
}
Now for this purpose I have files A.h/A.m B.h/B.m , C.h/C.m
Now I have a separate file G.h which has a number of global variables which are used by all the class A,B,C
Now I have imported B.h , C.h , G.h inside A.h . I also have separately imported the file G.h inside B.h and C.h
Now when build the project , I receive a link error for duplicate symbol on the global varibles (which is probably due to multiple inclusion of the file G.h) .
How can I solve this ??? The following is my code structure
//A.h
#import "B.h"
#import "C.h"
#import "G.h"
#interface A : NSObject {
B *b;
C *c;
}
//B.h
#import "G.h"
//C.h
#import "G.h"
//G.h
A *a=nil;
#interface G : NSObject { //whole class is empty}
You should declare your globals in G.h but define them in G.m, so that they're only linked once in the target. So if, for example, you have a string constant that's globally defined, you would do:
G.h
extern NSString const *appName;
G.m
NSString const *appName = #"My Great App";
In your pasted code, it's the A instance that's being duplicated. It's imported into each other translation unit, but with the same name every time which means that the compiled objects can't be linked. Assuming you want a shared A instance called a, you'd do:
G.h
#class A;
extern A *a;
G.m
#import "A.h"
#import "G.h"
A *a = nil;
Use the #class compiler directive in your class headers instead of importing the headers of the other classes. #class provides a forward declaration of a given class, so you can defer importing its header until you really need it, which is usually going to be in the corresponding .m file.
So instead of something like this:
#import "B.h"
#interface A : NSObject
{
B someObj;
}
...do this
#class B;
#interface A : NSObject
{
B someObj;
}
Now the compiler will understand that B is the name of a class, and therefore allow you to use it as a data type without importing B's header.
Related
I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end
I want to create a class that will contains static values accessable from all project.
Pseudocode:
class Constants:
constant String API_URL : "http://api.service.com"
constant Integer SOME_VALUE : 7
How can I do this with Objective-C ?
Answer for your question is extern keyword . I will explain it to you using an example . Add objective c classes your project and name them Common , Now in Common.h
#interface Common : NSObject
extern NSString *SiteApiURL;
#end
After you defined an instance of NSString Class using the extern keyword what you need to do is switch to Common.m class and initialize the value for NSString (SiteApiURL)
#import "Common.h"
#implementation Common
NSString *SiteApiURL = #"http://api.service.com";
#end
Import the Common.h class within the project-Prefix.pch file like this
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Common.h"
#endif
All done , now you can use the object "SiteApiURL" anywhere in the whole project and you need not to import any class anywhere i.e. You can use this variable anywhere in the project directly.
You could do it using preprocessors:
#define API_URL #"http://api.service.com"
#define SOME_VALUE (7)
Accessing defines would be simple: [object do:API_URL];
Or you could use constants
NSString * const apiURL = #"http://api.service.com";
NSNumber * const someValue = #7;
Accessing consts would be like accessing variables, So the string would just be a simple call. The NSNumber is an object wrapper for primitives so you'd need to access it like: someValue.intValue
You can create a Singleton with all necessary constants Here is a sample
If you do not want to create the class than you can use static private variables and static getters.
#interface
+(NSString*) getValue;
#end
#implementation
static NSString *_value = #"....";
+(NSString*) getValue {
return _value;
}
#end
I am new to iOS and obective-c so I am not too sure how to best accomplish this seemingly simple task.
What I want is to make a class that looks like this in pseudocode:
class UtilityClass
{
// Have a method that I can pass parameters to
String doCalculation ( String art1 , String arg2 )
{
return arg1 + arg2;
}
}
My uncertainty is:
1) xCode seems to be inclined to lay out my file structure in a relatively flat way. So should I make a utils directory and have this file be under utils/fileName ? Usually I am kind of used to having at least some src directory, but so far I have not been prompted by anything to create one.
2) How do I import and call this class/function from my controllers?
Thanks,
Alex
Just create a new group called Utilities, and then create your class inside it. Like,
utils.h
utils.m
Later in your ViewController's header file just add.
#import "utils.h"
if this utils class is used by many controllers in very fat project then, find a file called, Should be inside supporting files group.
YourAppName-Prefix.pch
In that file you have a code block like this,
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
Just edit this block and add your utils.h reference here, In this way your entire project can create utils object without explicitly importing into their own header.
Edit like this.,
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "utils.h"
#endif
First of all create a new File in Xcode and uncheck xib file. Name the Project as you like . and extend it from NSObject .
for creating static method you have to replace function starting - to + like
interface. class
#interface Utility : NSObject
+ (int)getNumber;
+ (void)setNumber:(int)number;
#end
.m class
#import "Utility.h"
#implementation Utility
static int number = 1;
+ (int)getNumber {
return number;
}
+ (void)setNumber:(int)newNumber {
number = newNumber;
}
+ (id)alloc {
[NSException raise:#"Cannot be instantiated!" format:#"Static class 'ClassName' cannot be instantiated!"];
return nil;
}
#end
call it in any other ViewController like
NSLog(#"number = %d", [Utility getNumber]);
[Utility setNumber:3];
NSLog(#"number = %d", [Utility getNumber]);
for details..
Where you store the files is up to you, just make sure XCode knows where to find them. The class itself should be made like any other Objective C class, just make it inherit from NSObject instead of one of the graphical classes:
// MyClass.h
#interface MyClass : NSObject {
int instanceVar;
}
#property (nonatomic, assign) int property;
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize property;
-(id) init {
...
}
-(int) function {
...
}
#end
To use the class in another file, just import the header like any other class
#import "MyClass.h"
I have two classes ClassA and Class B (they are viewControllers).
Class A is a delegate of classB.
ClassA "laucnhes" and instance of ClassB.
ClassB call methods on classA.
Let's say it's :
#import "ClassB.h"
#interface ClassA : NSObject {
ClassB* subController;
}
- (void) doThis;
-------------------------------
#import "ClassA.h"
#interface ClassB : NSObject {
ClassA* delegate;
}
-------------------------------
#implementation ClassB
- (void) someMethod {
AnObject* myObj = [self.delegate.arr objectAtIndex:8];
[self.delegate doThis];
}
Doing that way, A must import B, and B must import A.
If B do not import A (with just #class A), there is a compile error for the used attribute from A.
If B imports A, there is a compile error on the ClassA* delegateline.
Why do I have those compile errors? Doesn't #import protect again recursive calls ?
I don't need a solution to solve that problem, I know how I may do this.
But I wonder why my #import cause those problems. These are not #includes...
In .h files prefer #class to #import. Both can then be imported in the .m implementation files.
// ClassA.h -------------------------------
#class ClassB;
#interface ClassA : NSObject {
ClassB* subController;
}
- (void) doThis;
// ClassB.h -------------------------------
#class ClassA;
#interface ClassB : NSObject {
ClassA* delegate;
}
// ClassB.m -------------------------------
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassB
- (void) someMethod {
AnObject* myObj = [self.delegate.arr objectAtIndex:8];
[self.delegate doThis];
}
Using #class statements instead of #import also reduces dependencies and makes the remaining ones more clear. It can also speed up compile times.
Why do I have those compile errors? Doesn't #import protect again recursive calls ?
#import protects against repeatedly importing the same header into the same module, whether by circular includes/imports or not. It protects against that by not letting you do it: Only the first #import of a header works; subsequent #imports of the same header are ignored.
In a circular #include situation, the preprocessor would go around the circle some number of times and then fail the build before you even get to compilation. Using #import prevents the preprocessor from getting wedged and lets the preprocessor succeed, but circular-#import code is still dodgy at best and usually will not compile.
So, on to your specific situation.
For the code you showed in your question, #class will work in either or both headers, and indeed you should use it in both. You'll also need to #import both headers in both .m files.
If B do not import A (with just #class A), there is a compile error for the used attribute from A.
If you mean “there is a compile error at each point where I use that attribute of type ClassA *”, then yes: You can't talk to that object because you haven't imported its interface, so the compiler doesn't know what messages you can send to a ClassA instance. That's why you need to import its interface.
If B imports A, there is a compile error on the ClassA* delegateline.
If both headers import each other, then you have this:
ClassA.m:
ClassA.h
ClassB.h
ClassA.h (ignored because this was already imported by ClassA.m)
ClassB.m:
ClassB.h
ClassA.h
ClassB.h (ignored because this was already imported by ClassB.m)
There is no way this can work without one interface preceding the other without the other interface preceding it. That's the circle you're running into—the circle that #import exists to break. #include allows the circle, and thereby gets wedged:
ClassA.m:
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
(fails at some point)
Hence #import.
So you cannot import each header from the other. Hence #class.
But you still need to import each header from each module. That is, in fact, exactly what you need to do: Use #class in each header and use #import (on both headers) in each module.
This compile complaint can be avoided by declaring
#class ClassB;
in the .h file. The ClassB.h can then be included into the .m file.
So you are right on that one. Contrary to urban myth, #imports work pretty much like #includes in the sense that the compiler has to check the file.
See this (duplicate?) question for your philosophical problem.
I think you'll find that #import only protects against multiple inclusion once it has already been successfully included once, so to speak.
ie, in your case, it hasn't successfully imported classa.h before it is asked to import it again, so it does so.
Let’s say you wish to have two classes like so, each declaring an instance variable that is of the other’s class.
#interface A : NSObject {
B *something;
}
#end
#interface B : NSObject {
A *something;
}
#end
It seems to be impossible to declare these classes with these instance variables. In order for A to include an IV of class B, B must already be compiled, and so its #interface must come before A’s. But A’s must be put before B’s for the same reason.
Putting the two class declarations in separate files, and #import-ing each other’s ‘.h’s doesn’t work either, for obvious reasons.
So, what is the solution? Or is this either (1) impossible or (2) indicative of a bad design anyway?
To do that you need to use forward class declaration:
//a.h
#class B;
#interface A : NSObject {
B *something;
}
#end
// b.h
#class A
#interface B : NSObject {
A *something;
}
#end
So compiler will know that A and B are classes. And in implementation files for both classes just include a.h and b.h headers.
This is what the #class keyword is for. It tells the compiler that something is used as a class without the compiler necessarily knowing anything about it.
#class B;
#interface A : NSObject {
B *something;
}
#end
#class A;
#interface B : NSObject {
A *something;
}
#end