Object-C Linker command failed with exit code 1 - ios

I'm very new to Object-C programming and got an assignment. While building , the build failed with linker error due to duplicate symbols. I did my own analysis seems like it broke because , in one of the class file we have this :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return kCellHeight;
}
And in another class we have this:
self.contentView.frame = CGRectMake(5,5,[[UIScreen mainScreen] applicationFrame].size.width-10,kCellHeight - 5);
where as the kCellHeight has been defined in an interface as :
NSInteger const kCellHeight = 100;
Is that correct analysis?
Development Env:
XCode: 7.3.1
Target SDK : 7.1
Thanks in advance..
-S

Perhaps (although it would be easier to tell if you gave the compiler error). I assume that when you say "kCellHeight has been defined in an interface" you mean in a header file? Constants should be declared in a header file, but defined in an implementation file. Like this:
/* in .h file */
extern const NSInteger kCellHeight;
/* in .m file */
const NSInteger kCellHeight = 100;
If defined in a header file, kCellHeight will be defined whenever that header file is imported, which will result in multiple definitions (and a linker error).

Related

In iOS how to use NS_ENUM?

I know that such questions are asked before. I looked on the net find examples but I can not figure out how to use NS_ENUM in my code.
I have a list of error codes. To manage them I want to use NS_ENUM. I create a class which contains all my public const for the application. I want to define my enum here and use it everywhere in the app.
My code in .h file:
typedef NS_ENUM(NSInteger, wsErrEnum);
In my .m file:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
I thought that I can check in the following manner:
if(result.ErrCode == wsErrEnum.ERR_NONE) {
// do stuff ...
}
, but ERR_NONE is not visible as property.
So my question is how to define and use NS_ENUM in this case.
EDIT:
Using
if(result.ErrCode == ERR_NONE) {
NSLog(#"It is OK!");
}
give me error that ERR_NONE is undefined.
EDIT 2:
I understand the problem. I declare NS_ENUM in my .h file (as some comments and answers suggest) and the error disappears. I try this before but in that case I did not use enum properly - I use is as wsErrEnum.ERR_NONE. 10x for help.
P.S. My class is #imported in the .m file where I try to make this comparison.
The only problem here is that you've declared the enum's values in your .m file.
You should declare the entire enum (values included) in your .h file. By declaring the enum type wsErrNum in your .h file and the values in your .m file, you've hidden the values from all other classes.
So, in your .h file:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
Nothing needs to go in your .m file to declare this enum.
In Swift :
if result.ErrCode == wsErrEnum.ERR_NONE {
// do stuff ...
}
In Objective-C :
if(result.ErrCode == ERR_NONE) {
// do stuff ...
}
The real issue is that you have split up the declaration of wsErrEnum, remove it from the .m file and place the full delcaration in the .h:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
But better is something like:
typedef NS_ENUM(NSInteger, wsErrEnum) {
wsErrEnumName = 0,
wsErrEnumTimeOut = 1
};
Just a small remark, properties like your ErrCode should not started with a capital letter.
Normally, I use Enumerations in objective-c as typedef enum. Doing like this, you don't need an implementation file (.m) and the code looks like this:
// Enumerations.h
#interface Enumerations
typedef enum:int {
ERR_NONE,
ERR_HOST_TIMEOUT
} wsErrEnum;
#end
By default, the compiler values the enumerations from 0 to N, in order of which they are declared. In the example above, ERR_NONE is 0 and ERR_HOST_TIMEOUT is 1.
But you could also value the enumerations the way you preffer:
typedef enum:int {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 504
} wsErrEnum;
Then, you could work with switch and it would consider your enumeration, so it throws a warning in case you don't implement one possible result, for example:
switch(result.ErrCode) {
case ERR_NONE:
//implementation
break;
}
The code above will warn you that you don't have a case implemented for ERR_HOST_TIMEOUT.
You can also import your Enumerations.h in the file YourProjectName-Prefix.phc so it can be used in your hole project.
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#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 <CoreData/CoreData.h>
#import "Enumerations.h" //Add this line so you don't have to import your .h file every time you use it
#endif

How to use Objective-C code with #define macros in Swift

I'm trying to use a third-party Objective-C library in a Swift project of mine. I have the library successfully imported into Xcode, and I've made a <Project>-Bridging-Header.h file that's allowing me to use my Objective-C classes in Swift.
I seem to be running into one issue however: the Objective-C code includes a Constants.h file with the macro #define AD_SIZE CGSizeMake(320, 50). Importing Constants.h into my <Project>-Bridging-Header.h doesn't result in a global constant AD_SIZE that my Swift app can use.
I did some research and saw that the Apple documentation here under "Complex Macros" says that
“In Swift, you can use functions and generics to achieve the same
results [as complex macros] without any compromises. Therefore, the
complex macros that are in C and Objective-C source files are not made
available to your Swift code.”
After reading that, I got it to work fine by specifying let AD_SIZE = CGSizeMake(320, 50) in Swift, but I want to maintain future compatibility with the library in the event that these values change without me knowing.
Is there an easy fix for this in Swift or my bridging header? If not, is there a way to replace the #define AD_SIZE CGSizeMake(320, 50) in Constants.h and keep things backwards-compatible with any existing Objective-C apps that use the old AD_SIZE macro?
What I did is to create a class method that returns the #define.
Example:
.h file:
#define AD_SIZE CGSizeMake(320, 50)
+ (CGSize)adSize;
.m file:
+ (CGSize)adSize { return AD_SIZE; }
And in Swift:
Since this is a class method you can now use it almost as you would the #define.
If you change your #define macro - it will be reflected in the new method you created
In Swift:
let size = YourClass.adSize()
I resolved this by replacing
#define AD_SIZE CGSizeMake(320, 50)
in the library's Constants.h with
extern CGSize const AD_SIZE;
and adding
CGSize const AD_SIZE = { .width = 320.0f, .height = 50.0f };
in the library's Constants.m file.
write your constants after Class declaration. like this...
class ForgotPasswrdViewController: UIViewController {
let IS_IPHONE5 = fabs(UIScreen.mainScreen().bounds.size.height-568) < 1;
let Tag_iamTxtf = 101

Linker Command Failed with exit code 1: duplicate symbol - no .m file

I've seen this issue in other posts but in my case I am using a sphere.h file that contains the data of vertices of a sphere, that is, a 3D model for my game project. I import my sphere.h file into my objective-C Class as follows:
#import "SceneEnergyGlobe.h"
#import "sphere.h"
#interface SceneEnergyGlobe() {
}
#property (strong, nonatomic) GLKTextureInfo *textureInfo0;
#end
.....
When I compile my project, I receive this compile error. How I can fix this?
Here is the contents of sphere.h:
#ifndef SPHERE_HEADER
#define SPHERE_HEADER
unsigned int sphereNumVerts = 2280;
float sphereVerts [] = {
0.0743889747124915, -0.49384436095363, -0.0241703260695731,
0.190555012144643, -0.979722062440628, -0.0619150039460291,
0.000000, 0.95,
0.0632787269334132, -0.49384436095363, -0.0459747512867777,
0.162096012330563, -0.979722074526971, -0.11776900895863,
0.050000, 0.95,
0.125000004921036, -0.475528075643002, -0.0908176095396332,
0.269869905435848, -0.942722669663907, -0.196071931295133,
.....
For every implementation file (.m) that includes sphere.h a new copy of the vertices array will be created. When these implementation files are linked together you get your duplicate symbol error.
The best approach is to change this in sphere.h:
#define sphereNumVerts 2280
extern float sphereVerts[sphereNumVerts];
And add an implementation file (sphere.m) that contains the definition of sphereVerts:
#import "sphere.h"
float sphereVerts[sphereNumVerts] = {
0.0743889747124915, -0.49384436095363, -0.0241703260695731,
...
};
BTW, those look like big numbers for float; are you sure you don't want double?
(EDIT: My previous suggestion was wrong; you'll get duplicate symbols using const in a header file too. I have changed this to a #define).
It is a very very very bad idea to create and initialize variables in .h files!!!

Constants in Objective-C and "duplicate symbol" linker error

I've declared a constant with the same name in some different classes, in their .m file, this way:
#implementation MyViewController
const NSInteger numberOfItems = 6;
...
#end
But I get a "duplicate symbol" error when trying to build the project. I've found several posts dealing with this issue regarding extern or global constants, but what I'd want is just declaring some constants private to their class, how can I do that?
Thanks
If you want to use constant only in one .m file then declare it as static.
For example:static NSString * const CONSTANT_STRING = #"Constant I am".
In case of NSInteger you can write in your every .m file:
static const NSInteger my_const = 3;
If you want globals (one constant with one value visible in every file) then write in your .h:
extern const NSInteger my_global_const;
and in your .m file you can add
const NSInteger my_global_const = 5;

get error when to use global Variable

I have 2 class in my program
first class is class1 and second class is class2.I want create and initialize global variable in class 1 and to use in class 2 but compiler give me this ERROR XD :
Undefined symbols for architecture i386:
"_saeid", referenced from:
-[class2 viewDidLoad] in class2.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I create global variable in class1 and run that in class2 with this way but don't work:
class1.h
extern int saeid; // this is global variable
#interface class1 : UITableViewController<UITableViewDataSource,UITableViewDelegate>
#property (nonatomic,strong) IBOutlet UITableView *table;
#end
class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
{
int saeid;
}
#synthesize table;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int x = (indexPath.row)+1;
saeid = x; //initialize global variable
NSLog(#"X & SAEID: %d & %d",x,saeid);
}
class2.h
#import "class1.h"
#interface class2 : UIViewController<UIScrollViewDelegate>
{
}
#end
class2.m
#import "class2.h"
#implementation class2
{
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Saeid in class2 : %d",saeid);
}
There seems to be some confusion here. Most importantly, a global variable cannot be "in" a class--global variables are by definition outside of any classes. So if you really want a global variable (more on this later) then you need to take the int saeid; definition in class1.m outside of the class definition, and just have it at the file level.
After you've done that, things still won't compile. The statement extern int saeid; roughly says to the compiler "I've defined an integer named saeid somewhere else, so just pretend it exists and let the linker figure out how to hook it up." There is no reason to have this statement in class1.h because this global variable is not used anywhere in that file. Instead, you should put this extern statement near the top of class2.m. It is used in that file, so you need to assure the compiler that the variable is defined somewhere as it is compiling that file.
Those steps should get your code to compile. But now you should stop and think about whether or not you really want a global variable. Global variables tie your classes together and make it hard to change one without affecting (and possibly breaking) others. They make it harder to test your code, and they make it more confusing to read your code. Another option to consider here is to create saeid as a property on the class1 class, and add a class1* property to class2. Then when you create your class2 instance, pass along a pointer to the existing class1 instance. The class2 instance can keep that pointer and use it to access the saeid property as needed.
In Objectice-C you can't have class variables, just instance variables.
If you want to have a global var you'd write:
#import "class1.h"
#import "class2.h"
int saeid;
#implementation class1
{
}
#synthesize table;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int x = (indexPath.row)+1;
saeid = x; //initialize global variable
NSLog(#"X & SAEID: %d & %d",x,saeid);
}
But that's just a global var, it's got nothing to do with the class!

Resources