Arrangement:
While working on a application you may have used some predefined values. The visibility of these values can be application level, feature level or class level. For the convenience we give name to these values and then use in the application. In this case the name given by you only representing the single value and it never change to represent some other value thats why it is called Constant.
On the other hand variables are like us doing jobs in IT World and can switch from one organization(value) to other.
Like while choosing a partner for marriage you may choose best for you by seeing multiple persons and still after sometime you feel stuck. Same may happen you choose a best option to declare a constant and after sometime this constant can create a issue for you in code due to its scope or any other reason. On the other hand you may have a fixed mind that you always declare a constant in a same way in any situation like a love marriage and again after sometime you feel stuck. Even if you directly use literal without declaring a constant you will definitely face problem.
Visibility of constant:
When you declare a constant you must consider the visibility of it like is it class level, application level or feature level. However it does not effect the memory usage of the application but from the code readability and separation of concern you must declare it by taking care of visibility. It is also good for avoiding name conflict (duplicate symbol error).
Lets check how visibility changes for constant?
There are to two storage class for this:
1) Static
2) Extern (Default)
If you declare a constant with static keyword then it can only be refer in that particular file (Internal Linkage) and you cannot access it outside of that file. In this way the same name constant can exist in other file as well. On the other hand constant declare with extern keyword will be accessible from outside of the file(External linkage).
static NSString* sLiteralTest = @"staticConstant"; NSString* gLiteralTest = @"globalConstant"; @implementation LLVMIRTest{ NSString* ivarTest; } - (instancetype) init{ self = [super init]; ivarTest = sLiteralTest; return self; } @end
Clang output:
//global variable reference in LLVM IR @gLiteralTest = global %0* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %0*), align 8 //static variable reference in LLVM IR @sLiteralTest = internal global %0* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_.4 to %0*), align 8 //Ivar reference in LLVM IR @"OBJC_IVAR_$_LLVMIRTest.ivarTest" = hidden global i64 8, section "__DATA, __objc_ivar", align 8 @OBJC_METH_VAR_NAME_ = private global [9 x i8] c"ivarTest\00", section "__TEXT,__objc_methname,cstring_literals", align 1 @OBJC_METH_VAR_TYPE_ = private global [12 x i8] c"@\22NSString\22\00", section "__TEXT,__objc_methtype,cstring_literals", align 1
Clang generate module corresponding to each class or file in your codebase. Above Source code is part of the module generated by clang for the class LLVMIRTest. All the three variable is given different scope based on the linkage type specifier:
Private:
It specify that symbol is only accessible by objects in current module(class).
Internal:
It is same as private, But it treated as local symbol in module. You can directly access the symbol in the object file(static).
External(Default):
It means it is visible externally or globally. So it can refer to resolve external symbol reference.
MoreDetail:
Lets Meet the Bride (Literals):
literals are values which you directly used in source code like value of PI "3.14F", iOS Blogs Redirect Url Value is "https://cocoainios.blogspot.in". It can be string, Integer, Float or any other primitive data type.
Literals in memory:
Memory is one thing which you need to handle properly while doing programming or in real life as well. In both the cases it can crash your system if it is not handle properly. Let check how literal stored in memory:
There are four memory segment for a process:
1) Stack
2) Heap
3) Data
4) Code (Read only)
Which segment literal reside and for how much time?
No, Object is still store in heap and decided at run time. Let go through one by one and find out why the behaviour is different for them.
There are four memory segment for a process:
1) Stack
2) Heap
3) Data
4) Code (Read only)
Which segment literal reside and for how much time?
Literals are read only and stored in binary file. So when binary is loaded in code segment the literals are also loaded. If you use Identifier to refer a literal then memory of literal is depend upon the storage class you have used. It store in data segment if static keyword or extern keyword is used. Identifier can also used code segment memory if const keyword id used. However Identifier memory storage section also depend upon the compiler.
Hmmm!! That mean memory for array literal, dictionary literal are also decided at compile time ?
NSArray* literalArray = @[@"1", @"2", @"3"]; NSDictionary* literalDictionary = @{@1:@"first", @2:@"Second", @3:@"Third"};
No, Object is still store in heap and decided at run time. Let go through one by one and find out why the behaviour is different for them.
String Literals:
Almost in every programming language we are using string literals. Objective-c also support string literals with the help of class "NSConstantString". Memory for the Instance of this class is decided at compile time (statically allocate) and it occupied the memory for the full life cycle of application.
Hey! What? I have never declare any string in my code with class name "NSConstantString" then how this class come in scenario? I have always used NSString with following syntax:
NSString *myVar = @"TestString"; NSLog(@"%@", [myVar class]);
Objective-c uses abstract class feature for many of the foundation framework classes. Like NSArray,
NSDictionary etc. Same is applicable for NSString. When you declare any object with the reference name NSString then compiler will decide actual class of reference. Like in the above case it is NSConstantString string.
Let see some of the private class used by cocoa framework underneath NSString:
NSString* placeHolderString = [NSString alloc]; NSString* taggedPtrString = [[NSString alloc] initWithFormat:@"test"]; NSString* emptyString = [[NSString alloc] init]; NSMutableString* nsstring = [[NSMutableString alloc] init]; NSLog(@"%@", [placeHolderString class]); //NSPlaceholderString NSLog(@"%@", [taggedPtrString class]); //NSTaggedPointerString NSLog(@"%@", [emptyString class]); //__NSCFConstantString NSLog(@"%@", [nsstring class]); //__NSCFString
Let see how it decided memory at compile time and what it store in global space?
NSConstantString class interface is following:
@interface NSSimpleCString : NSString { @package char *bytes; int numBytes; #if __LP64__ int _unused; #endif } @end @interface NSConstantString : NSSimpleCString @end
It contains only pointer to bytes and length. Compiler can easily decide the memory size and embed the string into binary and initliaze the reference of this class with the required data.
NSString class is immutable class so in some cases compiler can do optimization and provide you the reference of constant class. Like if you allocated and init a String without any text then compiler will return you "NSCFConstantString" class reference.
Reference Handling String Literals:
1) Reference count of Const String is always -1.
2) Copy, Strong, Assign messages has same effect in case of literals. It will return you the same memory location without reference count update.
Let see some example where your compiler return you a constant string reference behind the scene:
NSString* emptyString = [[NSString alloc] init]; NSString* copyLiteral = [@"test" copy]; NSString* stringFromCopy = [NSString stringWithString:copyLiteral]; NSLog(@"%@", [emptyString class]); NSLog(@"%@", [copyLiteral class]); NSLog(@"%@", [stringFromCopy class]); //Result is __NSCFConstantString
Some Interesting Facts About String Literals:
1) @"a" == @"a" is always true. Single memory buffer is created to store literal.
2) Sending a copy message to string literal will return you same memory address.
1) @"a" == @"a" is always true. Single memory buffer is created to store literal.
2) Sending a copy message to string literal will return you same memory address.
NSString* literal = @"test"; NSString* copyLiteral = [literal copy]; (lldb) p literal (__NSCFConstantString *) $2 = 0x000000010b8273e0 @"test" (lldb) p copyLiteral (__NSCFConstantString *) $3 = 0x000000010b8273e0 @"test"
3) It reside in code memory.
4) Even a local to function/method String literal reference remain in memory for whole application life cycle.
Numeric Literals:
There are two ways you can define numeric literals:
1) NSNumber (Boxing):
Apple introduced new syntax in LLVM 4.0 to declare a number called as Number Literals. As we know Collection classes in objective-c does not support primitive data type you need to use Wrapper NSNumber class to hold these data type. New Syntax to declare NSNumber is easier way to declare a literals:
NSNumber *numberInt = @15; NSNumber *numberFloat = @15.6; NSNumber *numberChar = @'a';
However these are totally different from string literals. The memory for these object is not decided at compile time and these object created in a heap. So writing the above lines in your code is equivalent to write following:
NSNumber *numberInt = [NSNumber numberWithInt:15]; NSNumber *numberFloat = [NSNumber numberWithFloat:15.6]; NSNumber *numberChar = [NSNumber numberWithChar:'a'];
So these new syntax to store scaler value is just convenience method for writing code.
2) Inherited:
As Objective-C is superset of C so there is other way to declare numeric literals. You can directly use Primitive data type or Macro to declare numeric literals like given below:
static const int DOB = 15; static const float vat = 14.6; static const float initialChar = 'a'; #define DOB 15 #define vat 14.6 #define initialChar 'a'
Here in first case with const keyword we are directly using scaler type. So these are not objective-c Object and you cannot use these with collection classes.
However for memory point of view these follow the normal literals rule:
1) They always remain in memory for full application life cycle.
2) They value in stored in code segment and memory for identifier (Dob, vat, etc) against depend upon compiler.
Note: Macro itself not exist in memory they are only known to preprocessor after that only literals value (15, 14.6, 'a') exist in code memory.
This is also a convenient method to define a collection in your code and compiler will generate actual code for you. The compiler will generate following code for the above lines:
However for memory point of view these follow the normal literals rule:
1) They always remain in memory for full application life cycle.
2) They value in stored in code segment and memory for identifier (Dob, vat, etc) against depend upon compiler.
Note: Macro itself not exist in memory they are only known to preprocessor after that only literals value (15, 14.6, 'a') exist in code memory.
Collection Literals:
With NSNumber Literals Apple also introduced Array and dictionary literals. You can declare them by following syntax:
//Array Literal NSArray* literalArray = @[@"1", @"2", @"3"]; //Dictionary Literal NSDictionary* literalDictionary = @{@1:@"first", @2:@"Second", @3:@"Third"};
This is also a convenient method to define a collection in your code and compiler will generate actual code for you. The compiler will generate following code for the above lines:
//Array Literal NSArray* literalArray = @[@"1", @"2", @"3"]; //Compiler Generate id objects[] = {@"1", @"2", @"3"}; NSUInteger count = sizeof(objects) / sizeof(id); literalArray = [NSArray arrayWithObjects:objects count:count]; //Dictionary Literal NSDictionary* literalDictionary = @{@1:@"first", @2:@"Second", @3:@"Third"}; // Compiler generates: id objects[] = { @"first", @"Second", @"Third"}; id keys[] = { @1, @2, @3 }; NSUInteger count = sizeof(objects) / sizeof(id); literalDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count];
So collection literals are also like NSNumber. They are actual array and dictionary objects and memory decided at run time.
Hmmm! That is ok But why apple did not introduced these literals like String literals?
Ok let suppose you have to introduced such literals in existing framework. First you need to create a new class to support compile time memory decision. Let support you have created NSCFConstantArray, NSCFConstantDictionary with decided structure so that the memory footprint of class can easily be decided like NSCFConstantString. That it you are done with that.
Wait ! Can these classes actually behave like dictionary and array?
No Dictionary and Array can hold other object as well like NSObject, NSNumber etc which are not compile time objects i.e you cannot decide memory of these object at compile time. So It may be the reason these collection object is not behave like constant string.
Let Move To Venue:
In objective-c there are two ways to define a constant(never change value) .
(i) Macro
(ii) Const keyword
Macro:
(i) Macro
(ii) Const keyword
Macro:
In Objective-C macro can be used to define a constant. However it only exist for preprocessor and compiler does not know anything about the macros. Preprocessor replace all the occurrence of macro with the given text. So it is like find and replace feature to your text editor.
Const:
With the Const keyword you are actually declaring a variable which will exist for compiler as well as run time environment.
Let see some of the example to declare constant:
Const:
With the Const keyword you are actually declaring a variable which will exist for compiler as well as run time environment.
Let see some of the example to declare constant:
//Macro #define Number_Value @10 #define String_Value @"test" #define Scaler_Value 10 //Const Keyword static const NSString* name = @"test"; static const NSUInteger dob = 10;
Ok Lets try to declare a const with NSNumber, NSDictionary or NSArray?
Ohhh! Compiler error
Reason? Lets think and add in comment section.
Which approach is better const or #define ?
You may think that macro is good because you are saving space and time by not declaring a variable but some smart compiler also do the same thing with const. They just replace all occurrence of const with the provided text or value.
On the other hand using const has some advantage over macro:
1) Type Checking and obey the scope principal is big advantage in case of using const. When you are declaring a variable with const keyword then you also specify the type of the identifier. So compiler can easily check if there is any type mismatch and give you warning.
2) It makes debugging easy. You can check the value at run time for the variable and debug the code.
3) Macros are more error prone and sometime difficult to find mistake. Because compiler do not have any reference for the Macro. Like in the following code:
#define DISTANCE_TRAVELLED 10.0; #define TIME_TAKEN 2; NSTimeInterval time = (DISTANCE_TRAVELLED / TIME_TAKEN); //Error Expected ')'
One cannot easily find out the mistake by seeing the error description. In larger codebase it can take hours to simple find out these type of error. Like duplicate definition of macro does not give you any error and you will not find out that easily.
//Abc.h #define DOB 15 //Xyz.m #import "abc.h" #define DOB 10 //No error only warning depend upon compiler
Whats Next?:
Lets enjoy the food now : ).
Yes, Try to declare a constants keeping the above points and based on your requirement use any way.
Comments
Post a Comment