Using extern for static variables - objective-c

I'm seeing something odd when I run some code that uses the extern keyword to reference a static variable within the implementation file. So I declare the static variable gCounter within my implementation file and reference it within two methods in the same implementation file (because its static). However, when i use the extern keyword in my methods i get different results. My understanding (from reading my book) is that extern isn't necessary when you're referencing a static variable declared in the same file as your methods. Code is as follows:
/** interface **/
#import <Foundation/Foundation.h>
#interface Fraction : NSObject
+(Fraction *) allocF;
+(int) count;
#end
/**implementation**/
#import "Fraction.h"
static int gCounter;
#implementation Fraction
+(Fraction *) allocF
{
extern int gCounter;
++gCounter;
return [Fraction alloc];
}
+(int)count
{
extern int gCounter;
return gCounter;
}
#end
/**main**/
#import "Fraction.h"
int main (int argc, const char * argv[])
{
#autoreleasepool
{
Fraction *a, *b, *c;
NSLog(#"The number of fractions allocated: %i", [Fraction count]);
a = [[Fraction allocF] init];
b = [[Fraction allocF] init];
c = [[Fraction allocF] init];
NSLog(#"The number of fractions allocated: %i", [Fraction count]);
}
return(0);
}
When I use the extern keyword in my methods, the code works properly and results in the integer 3 being printed. However, when I remove extern, the integer 2 gets printed. Why is that? Since gCounter is a static variable, shouldn't this work without the extern keyword?

You need to understand the difference between a declaration and a definition:
static int x and int x are definitions. The compiler reserves memory for x.
extern int x on the other hand is a declaration. You tell the compiler that there is a variable x that is defined somewhere else.
Also, you can define different variables in different scopes, that have the same variable name:
int x = 0;
{
int x = 1;
NSLog(#"x = %d", x); // x = 1
}
NSLog(#"x = %d", x); // x = 0
So if you write
int x;
void foo() {
int x;
x++;
}
you are incrementing the function local x.
int x;
void foo() {
x++;
}
increments the global x
int x;
void foo() {
extern int x;
x++;
}
You need to declare extern int x if your definition of x is in another compilation unit, if it's in the same compilation unit, the last two are equivalent.

Related

ObjectC-Why can't I get the properties correctly using the class_copyPropertyList function?

macOS 11.5.2
Xcode 13.2.1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <iostream>
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"NSString");
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
const char* name = property_getName(properties[i]);
std::cout << name << std::endl;
}
free(properties);
}
return 0;
}
I will take some snippets of the output:
hash
superclass
description
debugDescription
hash
superclass
description
debugDescription
vertexID
sha224
NS_isSourceOver
hash
superclass
description
debugDescription
...
From the output, we can find that properties such as hash, description, superclass, etc. will appear repeatedly several times, while some properties (such as UTF8String) do not appear in the result list.
How should I get the list of properties correctly?
I would appreciate it.
The reason you're not seeing UTF8String come up as a property is that it's not declared as a property in the main declaration of NSString, but rather in a category. On macOS 12.2.1/Xcode 13.2.1, the declaration of NSString boils down to this:
#interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
#property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
#end
All other properties and methods on NSString are declared in categories immediately afterwards:
#interface NSString (NSStringExtensionMethods)
#pragma mark *** Substrings ***
/* To avoid breaking up character sequences such as Emoji, you can do:
[str substringFromIndex:[str rangeOfComposedCharacterSequenceAtIndex:index].location]
[str substringToIndex:NSMaxRange([str rangeOfComposedCharacterSequenceAtIndex:index])]
[str substringWithRange:[str rangeOfComposedCharacterSequencesForRange:range]
*/
- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
// ...
#property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation
// ...
#end
When a property is declared in a category on a type like this, it doesn't get emitted as an actual Obj-C property because categories can only add methods to classes, and not instance variables. When a category declares a property on a type, it must be backed by a method and not a traditional property.
You can see this with a custom class, too — on my machine,
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyClass: NSObject
#property (nullable, readonly) const char *direct_UTF8String NS_RETURNS_INNER_POINTER;
#end
#interface MyClass (Extensions)
#property (nullable, readonly) const char *category_UTF8String NS_RETURNS_INNER_POINTER;
#end
#implementation MyClass
- (const char *)direct_UTF8String {
return "Hello, world!";
}
- (const char *)category_UTF8String {
return "Hi there!";
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"MyClass");
printf("%s properties:\n", class_getName(clazz));
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
printf("%s\n", property_getName(properties[i]));
}
free(properties);
puts("-----------------------------------------------");
printf("%s methods:\n", class_getName(clazz));
Method *methods = class_copyMethodList(clazz, &count);
for (uint32_t i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
printf("%s\n", sel_getName(name));
}
free(methods);
}
return 0;
}
outputs
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
category_UTF8String
If you remove the actual implementations of the *UTF8String methods from the class, the property remains declared, but the category method disappears (because it doesn't actually have a synthesized implementation because of how categories work):
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
As for how to adjust to this: it depends on what purpose you're trying to fetch properties for, and why you might need UTF8String specifically.
NSString declares in its interface it implements methods, but it does not actually implement them, that is why when you print at runtime a list of the its methods it does not print what you expect.
The methods are implemented by other private classes, and when you initialize a new instance of NSString, instead of getting an instance of NSString you get an instance of that private class that have the actual implementation.
You can see that by printing the class type of a string, the following prints NSCFString or NSTaggedPointerString, not NSString:
NSString* aString = [NSString stringWithFormat: #"something"];
NSLog(#"%#", [aString class]);
And this prints __NSCFConstantString:
NSLog(#"%#", [#"a constant string" class]);
This pattern is called a class cluster pattern.
If you modify to dump the methods of the NSCFString you will get a "redactedDescription", it seems you are prevented to query these classes.

EXC_BAD_ACCESS runtime exception when accessing the structure variables->Objective C

test.h
-------
struct session {
int a;
int c;
int b;
};
struct session* pEvent;
#import <Foundation/Foundation.h>
#interface test : NSObject {
}
-(void)set;
#end
test.m
--------
#import "test.h"
#implementation test
-(id)init{
pEvent->a=10;
pEvent->c='a';
pEvent->b=20;
return self;
}
-(void)set{
//struct session* pEvent;
//pEvent->a=10;
//pEvent->c='a';
//pEvent->b=20;
NSLog(#"a:%d c:%c b:%d",pEvent->a,pEvent->c,pEvent->b);
}
#end
I am getting EXC_BAD_ACCESS runtime exception and the debugger points to pEvent->a when
declared in both the ways inside the init method or inside the set method.
Do i need to intialise the structure as pEvent = new session;? If declared like this I'm
getting new undeclared error. Then I tried with pEvent = [session new]; and pEvent = [[session alloc]init]; If declared like this i'm getting session undeclared error.
Try this:
struct session {
int a;
int c;
int b;
} pEventStruct;
struct session* pEvent = &pEventStruct;
You got bad access as you have not allocated memory for pEvent, just declared a pointer.
For a global structure you don't want a pointer unless you malloc, so like so:
struct session {
int a;
int c;
int b;
};
struct session pEvent;
Then pEvent.a = 10 to access.
A better design might be to use object-oriented patterns like singletons or a class method to access your data structure, however.

passing the static variable as an argument->Objective C

c++ code
--------
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
static int a;
void set(int s)
{
a=s;
cout<<a<<endl;
}
void setData(int f)
{
cout<<"I am "<<f<<" years old!!!"<<endl;
}
};
int A::a=0;
int main()
{
A* ab=new A();
ab->set(10);
ab->setData(ab->a);
return 0;
}
I am trying to get the same output for
this equivalent Objective C code.
main.m
---------
#import <Foundation/Foundation.h>
#import "A.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
A* ab = [[A alloc]init];
[ab set:10];
[ab setData:ab.a]; //getting error when passed ab->a or ab.a as an argument
[pool drain];
return 0;
}
A.h
---
#import <Foundation/Foundation.h>
#interface A : NSObject {
}
-(void)set:(int)s;
-(void)setData:(int)f;
#end
A.m
----
#import "A.h"
#implementation A
static int a;
-(void)set:(int)s
{
a=s;
NSLog(#"%d\n",a);
}
-(void)setData:(int)f
{
NSLog(#"%d",f);
}
#end
Error:Request for member 'a' in something not a structure or union.
There are no static instance variables or methods in Objective C. What you want can be done with class methods and static file scope variables. Class methods are those methods sent to class objects rather than instances.
#interface AClass
{
}
+(int) a;
+(void) setA: (int) newValue;
#end
// A.m
static int aStorage = 0;
#implementation AClass
+(int) a
{
return aStorage;
}
+(void) setA: (int) newValue
{
aStorage = newValue;
}
#end
// To use:
int something = [AClass a];
[AClass setA: something * 2];
// Or dot syntax if you prefer
AClass.a = AClass.a * 2;
First, declaring a static int in your implementation file doesn't magically make it a member of class A; your ObjC class A has no member variables.
Second, ab->a isn't how you would access the member of a class in ObjC. Once you have a member, write a getter for a, and use a method call to access it. (Or if you really want it to be static, don't add a member, and just add a getter that returns the static variable.)

static behaviour->objective C

I have this static declaration of m_pData=1099656 in a class.
In main NSLog(#"Testing:%d",m_pData);
Its printing me 0.
How to get the same value of m_pData in the class in the main function too.
I cant use obj.m_pData or obj->m_pData to acces the variable.
EDITED:
test2.m
----------
#import <Foundation/Foundation.h>
#import "data_derived.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
data* dat = [data alloc];
requestSession* session = [requestSession alloc];
[session init];
[dat TxCreateImage:RM_REQUEST_SESSION];
NSLog(#"Testing:%d",m_pData); //the static variable is not printing the value its holding.Its printing Zero.If printed the same variable inside the class it gives some numbers.
[dat dataBuffer:&m_pData withLen:&m_uDataSize]; //here the actual values of static values are not passed.when printed both of them contains zero values.
[pool drain];
return 0;
}
data.h
--------
#import <Foundation/Foundation.h>
#import "remote.h"
static int m_nMessageId; //Message ID
static int m_uSessionId; //Session ID
static int m_chSequenceChar; //Sequence ID
static int* m_pData; //Integer buffer to carry data
static int m_uDataSize; //Datasize
#interface data : NSObject {
#public
}
- (id)initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize;
+ (void)initialize;
- (void)dealloc;
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen;
- (BOOL) TxCreateImage:(int)messageId;
#end
data.m
---------
#import "data.h"
#define ENCODED_MSG_DATA_OFFSET 8
#implementation data
+ (void)initialize
{
m_uSessionId = 0;
m_chSequenceChar= 0;
// Initialize values from derived class
m_nMessageId = 0;
m_pData = 0;
m_uDataSize = 0;
}
- (id) initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize
{
if(self=[super init])
{
// Initialize the member variables
m_uSessionId = 0xFF;
m_chSequenceChar= 10;
// Initialize values from derived class
m_nMessageId = uMessageId;
m_pData = (int*)pData;
m_uDataSize = (int)uDataSize;
}
NSLog(#"Data size:%d",uDataSize);
NSLog(#"m_pData:%d",m_pData);
NSLog(#"pData:%d",pData);
return self;
}
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen
{
if ( m_uDataSize <= RMH_MAX_ENCODED_LENGTH )
{
int abBigEndian[RMH_MESSAGE_MAX_SIZE];
memcpy(abBigEndian,m_Data,m_uDataSize);
NSLog(#"m_Data:%d",*m_Data);
NSLog(#"abBigEndian:%d",abBigEndian);
uLen += ENCODED_CRC_BYTE_LEN + 1;
NSLog(#"%d",*uLen);
}
NSLog(#"END!");
return self;
}
- (BOOL) TxCreateImage:(int)messageId
{
char pData[4096];
sprintf(pData,"%x %d %d %d %x",ASCII_STX,m_uSessionId,m_chSequenceChar,m_nMessageId,ASCII_ETX); //uLen = ENCODED_MSG_DATA_OFFSET;
NSLog(#"%s",pData);
return YES;
}
- (void)dealloc
{
[super dealloc];
}
#end
data_derived.h
---------------------
#import <Foundation/Foundation.h>
#import "data.h"
#define DECLARE_RS232_NEWMSG(ClassID)\
enum \
{ \
ID = ClassID \
}; \
#interface requestSession : data {
#public
DECLARE_RS232_NEWMSG(RM_REQUEST_SESSION);
struct RMH_REQUEST_SESSION_MSG st;
}
-(id)init;
-(void)dealloc;
#end
data_derived.m
---------------------
#import "data_derived.h"
#implementation requestSession
- (id)init
{
size_t asize = sizeof(st);
st.uDeviceID = RS232_PROTOCOL_DEVICE_ID;
st.uProtocolVersion = RS232_VERSION;
memset(st.uReserved,0x00,sizeof(st.uReserved));
NSLog(#"Address of the structure:%d",&st);
self=[super initWithID:ID withData:(id)&st withSize:asize];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
Don't place your static members in .h file. Move them to data.m.
Also about static keyword - in C (and Objective C, which is clear superset to C), when using static with global variables (like you do), it only indicates that this variables will be local to the file you declared these variable in. So global variables anyway have only one instance, with static modifier or without. If you don't use static, then you can access these variables from other files with external declaration, like:
// file1.m
int variable = 4;
// file2.m
external int variable; // variable == 4
That's why your code is printing you 0. m_pData in test2.m is not the same m_pData you have in data.m. Without static modifier you would get a linker error.
And you might want to write getters/setters for static members. Like:
+ (int *)pData {
return m_pData; // or smth like memcpy
}

Objective C...Define structures?

I am new programming and Objective-C in general. Do I declare a struct in the interface and define it in the implementation use a void method ?...
#interface Elements : NSObject {
struct elementZ{
NSString *name;
float molarmass;
}elements[81];
Now that I have declared it..where do I initialize the elements[81] array?
Your code works as is for me. Then you can access the data in your implementation like so:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
elements[2].name = #"hello";
NSLog(elements[2].name);
}
This will print 'hello' to the console.
Do I declare a struct in the interface and define it in the implementation use a void method ?
Yes it is what you think since Objective C is a super set of C. Have a method, that takes no arguments and returns void and initialize the member variables of struct elementz through elements[81];. Probably by running a loop from 0 to 80. As an example -
#import <Foundation/NSObject.h>
#interface Fraction: NSObject {
struct myStructure {
int numerator, denominatior;
}objects[10];
}
-(void) print;
-(void) initialize;
#end
#implementation Fraction
-(void) initialize
{
for( int i=0; i<10; ++i )
{
objects[i].numerator = (i+1);
objects[i].denominatior = (i+2);
}
}
-(void) print
{
for( int i=0; i<10; ++i )
{
printf("%d\t%d\n", objects[i].numerator, objects[i].denominatior);
}
}
#end
int main(int argc, char *argv[])
{
Fraction *obj = [[Fraction alloc] init];
[ obj initialize ];
[ obj print ];
[ obj release ];
return 0;
}
Output:
[Switching to process 949]
Running…
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
Debugger stopped.
Program exited with status value:0.
Hope it helps !
typedef struct element{
NSString *name;
float molarmass;
}elementZ;
elementZ[81] array;
Add a init method within the implementation block of your Elements class:
- (id)init {
if (self = [super init]) {
for (int i=0; i<81; i++) {
elements[i].name = "foo";
}
}
return self;
}
maybe some reading is required to get familar to Objective-C: Introduction to The Objective-C Programming Language
Object creation in Objective-C requires two steps:
Allocation
Initialization
E.g.:
Elements *e = [[Elements alloc] init];