Objective-C passing object to function is always by reference or or by value? - ios

In objective-c I am passing NSMutableDictionary to function and modifying it inside function it returns modified mutable dictionary :
NSMutableDictionary *obj2 = [[NSMutableDictionary alloc]initWithObjectsAndKeys:#"hello",#"fname",nil];
[self callerDictionary:obj2];
NSLog(#"%#",obj2[#"fname"]);//printing "Hi"
-(void)callerDictionary:(NSMutableDictionary*)obj
{
obj[#"fname"] = #"Hi";
}

Technically, Objective C always passes parameter by value, as does C, but practically when you pass an object you need to pass a pointer. While this pointer is passed by value, the semantics of Objective-C give the same effect as if you had passed an object reference; if you modify the objected that is pointed to by the pointer then you are modifying the same object instance that is pointed to in the calling context. The common terminology used in Objective C programming is "object reference" even though it is really a pointer value.
You can see from the * in the method signature that it is a pointer (or object reference in the common usage). If you are passing an intrinsic type, such as an int then it is passed by value unless you explicitly declare the method as requiring a reference:
For example:
-(void) someFunction:(int *)intPointer {
*intPointer = 5;
}
would be called as
int someInteger = 0;
[self someFunction: &someInteger];
// someInteger is now 5
The distinction between a pointer value and a true object reference can be seen in comparison to Swift which uses true references;
If I have
-(void)someFunction:(NSString *)someString {
int length = [someString length];
}
and then do
NSMutableArray *array = [NSMutableArray new];
[someFunction: (NSString *)array];
I will get a runtime exception since array doesn't have a length method, but the compiler can't confirm the type I am passing since it is a pointer.
If I attempted the equivalent in Swift then I will get a compile time error since it knows that the type coercion will always fail

All objects in Objective C passed by reference.
All C types such as NSUInteger, double etc. passed by value

C and Objective-C always pass parameters by value. Objective-C objects are always accessed through a reference (i.e. a pointer). There is a difference between a variable type (int, pointer, etc.) and the way variables are passed as function parameters. The use of the term reference in both scenarios can cause confusion.
by-value:
void f(int a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 5
The value 5 is printed both times because the function f() is given a copy of the value of a, which is 5. The variable referenced within the function is not the same variable that was passed in; it is a copy.
In C++, you can have functions that take parameters by reference.
by-reference:
void f(int &a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 14
Note the & in the function signature. In C++ (but not C, nor Objective-C), this means that the parameter is passed by reference. What this means is that a reference (pointer) to a is passed to the function. Within the function, the a variable is implicitly dereferenced (remember, it's really a pointer, but you don't treat it as one), and the original a variable declared outside the function is changed.
In C and Objective-C, passing a pointer to a function is functionally equivalent to using a reference parameter in C++. This is because a copy of the address is given to the function (remember, the parameter is still passed by value), and that address points to the same object instance that the original pointer does. The reason you don't see any explicit pointer dereferencing within the function (similar to the C++ reference) is because Objective-C syntax for object access always implicitly dereferences -- being within a function doesn't change this behavior.

Related

Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'

Hi I am learner in Objective-c Having a warning of Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'
- (void)stopAnimating {
pause = YES;
if (timerArray) {
for (NSInteger i = 0; i < [timerArray count]; i++) {
dispatch_source_t _timer = [[timerArray objectAtIndex:i] source];
dispatch_source_cancel(_timer);
_timer = nil;
}
timerArray = nil;
}
[self removeAllFlakesWithAnimation:YES];
}
in dispatch_source_t _timer = [[timerArray objectAtIndex:i] source]; this line, how to solve, timeArray is a NSMutableArray NSMutableArray *timerArray;
We can't tell you what is wrong with your code, there is not enough information for that, but we can tell you what the compiler is doing and why it produces the error it does – then you'll have to resolve it from there.
In your line:
dispatch_source_t _timer = [[timerArray objectAtIndex:i] source];
The LHS declares are variable, _timer, of type dispatch_source_t so the RHS needs to return a value of this type. Let's look at the RHS:
[timerArray objectAtIndex:i]
which BTW you can write more succinctly as:
timerArray[i]
this indexes into an array which you have declared as:
NSMutableArray *timerArray;
the elements of an array like this have type id – which means a reference to any object. The actual type of the objects in the array in this case will not be known until runtime. The next part of the RHS is:
[<a reference so some object> source]
Objective-C allows this and will perform a check at runtime to determine that the reference object does indeed have a method source. However at compile time the compiler can look up the definition of methods called source, it does, and finds that the method returns an NSString *.
So the RHS returns an NSString * and the LHS requires an dispatch_source_t and therefore the compiler reports:
Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'
Now you have to figure out whether you intended to call source or some other method which does return a value of the right type, etc. HTH
As another BTW to someone learning Objective-C: You are using a for loop to produce an index value for an array, and you only use that value to index the array once. A better way to do this is to use a for/in loop:
for (<YourObjectType> element in timerArray) {
dispatch_source_cancel([element source]);
}
You need to replace <YourObjectType> with the type of object references you've stored in timerArray, and as above the source method needs to return a dispatch_source_t value.
Objective-C has a for ... but there are other really nice ways in which you can iterate through elements of an array. I give one example for array a, in this case of NSString *
[a enumerateObjectsUsingBlock: ^ ( NSString * i, NSUInteger idx, BOOL * stop ) {
// do something with NSString * i
// its index into the array is idx if you need it
// to exit out of the loop do
* stop = YES;
}];

Passing an object that undergoes default argument promotion to 'va_start'

This is my first Xcode app and objective-c so give me some slack :)
I tried googling on the issue but I cannot see any help regarding Xcode and app development. I added the error masseages after //
- (id)initWithBytes:(int8_t)byte1, ... { //Error: 1. Parameter of type 'int8_t' (aka 'signed char') is declared here
va_list args;
va_start(args, byte1); //Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
unsigned int length = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
length++;
}
va_end(args);
if ((self = [self initWithLength:length]) && (length > 0)) {
va_list args;
va_start(args, byte1); // Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
int i = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
_array[i++] = byte;
}
va_end(args);
}
return self;
}
Thank you in advance!!
va_start() saves the pointer to the first argument passed to the function into a va_list.
The arguments themselves are passed via a hardware stack.
The issue with int8_t comes from the way the hardware stack is implemented. (in x86 at least)
Just like the SSE and MMX does, the stack requires elements stored on it to have an alignment equal to a multiple of 16bits, so everything passed to the function WILL have at least 16 bits of size, regardless of its type.
But the problem is va_arg() doesn't know about all that. Historically, it was a macro, and all it does is returning a pointer stored in va_list, and incrementing va_list by sizeof(type).
So, when you retrieve the next argument, the pointer returned does not point to the next argument but one byte before it, or not - depending on whether the va_arg is a macro or a compiler built-in function.
And this is what a warning is about.
IMO at least. Pardon my English, It's my 2nd language.

Get the data stored in address using the * operator

int i = 17;
int *addressOfI = &i;
printf("the int stored at addressOfI is %d\n", *addressOfI);
The question is: If I can get the data stored in addressOfI using the * operator, why it doesn't works for type NSString? like following:
NSString *string = #"Hello world!"
printf("the NSString stored at string is %#\n", *string);
why it doesn't works for type NSString?
Because NSString is an Objective-C object and not a primitive type. The NSString * pointer actually points to a struct objc_object which provides the framework for the object system. You can probably "see" some primitive types within this framework (i.e. members of objc_object) however it's supposed to be a black box to normal developers.
The actual reason your second piece of code will crash is that the %# format specifier expects to call the description method on the object you pass in as an argument and you have dereferenced that object pointer so it's no longer a valid object pointer.

BOOL property from a calculation returns NSNumber with incorect value using valueForKey:

I have a simple object which has one NSNumber which is used to store some flags.
I have a conienience getter method which in fact does:
[self.flags integerValue] & SomeConstantFlag
for a property#property (readonly, nonatomic, assign) BOOL someConstantFlag
and this works fine when accesing the underlying bool value like
model.someConstantFlag
but when I try to
id value = [model valueForKey:#"someConstantFlag"];
Then it returns a bad boolean representation e.g. NSNumber with value 2, 4 etc.
Why is this happening when the declaration of the property is BOOL? Is there a "Pretty" way to overcome this issue?
Wrapping on the other hand works ok:
BOOL someBool = 42;
NSNumber* numberVal = #(someBool);
//The underlying is an __NSCFBoolean with the proper 0/1 val!
valueForKey always returns an Objective-C object, even if the property has scalar type.
From the documentation (emphasis mine):
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
The return value of your method is BOOL, which is defined as
typedef signed char BOOL;
on OS X and on the 32-bit iOS platform. So what valueForKey returns is a NSNumber
containing the result of
signed char val = [self.flags integerValue] & SomeConstantFlag;
and that can be in the range -128 .. 127.
To ensure that you get only YES or NO (aka 1 or 0) write your custom getter as:
-(BOOL)someConstantFlag
{
return ([self.flags integerValue] & SomeConstantFlag) != 0;
}
Remark: On the 64-bit iOS platform (but not on 64-bit OS X), BOOL is defined as the C99 _Bool, which is a "proper" boolean type and can take only the value 0 or 1.
NSNumber *value = #([model objectForKey:#"someConstantFlag"]);
BOOL boolVal = [value boolValue];
I think you should consider the following problems. Firstly, integerValue returns NSInteger which means if you support 64Bit architecture it will return int_64 not int_32, what is more in your code here
[self.flags integerValue] & SomeConstantFlag
this does the following if flags is 00010 and somConstantFlags is 00001 the & of those will do something you probably does not expect because you will get value of 00000 which equals 0 or if they are 00011 and 00110 you will get 00010 which equals 2. So that is why when you call valueForKey you get 2 or 4 or something else depending on your flags :)
What is more in objective-C everything different then 0 is YES.
Try reconsidering your bit logic :). See The following example
enum
{
kWhite = 0,
kBlue = 1 << 0,
kRed = 1 << 1,
kYellow = 1 << 2,
kBrown = 1 << 3,
};
typedef char ColorType;
and in your setter check the following
ColorType pinkColor = kWhite | kRed;
if (pinkColor & (kWhite | kBlue | kRed | kYellow)) {
// any of the flags has been set
}
The flags kWhite, kBlue, kRed and kYellow have been set.
However, kBrown has not been set.

Setting Integer value in Objective c

I have recently started programming in iOS.. I am going through a code snippet that declares the following variables:
int rc = 0X00;
sqlite3_stmt *pStmt = 0X00;
FMStatement *stat = 0X00;
BOOL abc = 0X00;
what does this mean?? I read somewhere that setting 0X00 in a reference variable means setting it to NULL (in C). But what does setting a BOOL type variable and an int type variable to 0X00 mean??
I suggest you read up about the basics of programming languages, specifically, C programing with pointers. Objective-C is a superset of C and follows many similar rules.
But to your question:
The 0x in front of the literal values in the code (0x00) specifies that the value is interpreted as hexadecimal rather than decimal. But 0x00(hex) is the same as 0(dec).
int rc = 0x00; //same as int rc = 0;
int is a primitive type in both Obj-C and C that specifies an integer, effectively you are initializing the variable. In the C language you must initialize variables otherwise they could be pointing at a random piece of memory.
Therefore, examine this code:
int a;
int b = 0;
//a is NOT equal to b!
In C, the variable 'a' has not be initialized and therefore its not typically safe to assume that it will be initialized to 0. Always initialize your variable.
If you did a printf, or an NSLog of the variable 'a' you will see that it prints some huge number and it doesnt make sense (sometimes this is compiler dependent)
The same can be said for a BOOL. Although setting a BOOL to 0 is the same as setting it to false;
BOOL flag = 0; //The same as saying BOOL flag = false;
Now for the final part of your code:
FMStatement *stat = 0X00;
Often in Objective-C if you are dealing with pointers and objects you need to initialise the pointer to point at some memory address. The actual memory address is usually determined by the stack/heap and you don't need to worry about that. But you do need to ensure that the pointer isn't pointing to the wrong location (known as a garbage pointer).
To do this, we simply set our pointer to nil. eg:
FMStatement *stat = nil; //This pointer is now safe. Although memory still hasnt been allocated for it yet
This is usually taken care of for you though when you immediately allocate the memory for an object, therefore in this case you don't need to worry about initializing the pointer to nil:
FMStatement *stat = [[FMStatement alloc]init];
Like I said, I recommend you read about basic C programming, allocations, pointers, datatypes, initialising etc, once you have a grasp of this, then move to Objective-C which then builds ontop of it with Object-Oriented stuff.
Good luck.
0X00 is simply 0 in hexadecimal notation. So,
int rc = 0X00;
is the same as
int rc = 0;
Same for BOOL variables, where 0 is the same as NO. Using 0X00 is odd -- it'd make more sense to use 0 or NO where appropriate, and use nil for the pointers.

Resources