I would like to create a C array, which stores some objects but would like to declare it as follows:
id array = malloc(sizeof(NSObject * 4));
But this gives an error; it asks me to either:
Fix it - use __bridge to convert directly (No change in ownership).
Or:
Fixe it: use CFBridgeRelease to call to transfer ownership of a +1
'void' into ARC.
I have tried both, but it still gives an error:
Missing )
I remember having done this; but I forgot how since it has been a while.
How can I store ids in C array and retrieve things out of it and then cast them down?
The size of a pointer is the same for all types, including objects, so the following is all you need:
id *myArray = malloc(sizeof(void *) * 4);
Note that the type used on the left in the posted example was also incorrect, since the memory being allocated is expected to be referenced as a C array of pointers to objects, rather than just an object.
If you're compiling with ARC enabled, you'll need to add a lifetime qualifier to the declaration of myArray, and cast the return value of malloc. That's because ARC can only manage the lifetimes of pointers to objects and the array in which you're going to store the objects is a C type. For example, to tell ARC explicitly that the pointers in the array are unmanaged you could modify the previous code as follows:
__unsafe_unretained id *myArray = (__unsafe_unretained id *) malloc(sizeof(void *) * 4);
Note that since ARC can't manage retain counts in C arrays, it will be up to you to ensure that whatever you store in the array can be used safely.
You can't store arrays of objects in Objective-C. It only allow allocating pointers to objects. To do that, the right syntax would be like this:
id *array = malloc(sizeof(*array) * 4);
or possibly
id *array = malloc(sizeof(id) * 4);
but the former is more DRY.
Related
I am just starting to learn Objective-C. I am confused to see that some types of variables are sometimes declared with an * asterisk, others are not. For example these are delcared with a *:
#property NSString *firstName;
NSString * mainString = #"Hello World!";
NSNumber *longNumber = #42l;
NSArray *unsortedStrings = #[#"gammaString", #"alphaString", #"betaString"];
And these are not:
int someInteger = 42;
NSInteger anInteger = 64;
id firstObject = #"someString";
NSRange substringRange = [mainString rangeOfString:#"long"];
I found this explanation from Apple's documentation: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW1
Both these properties are for Objective-C objects, so they use an asterisk to indicate that they are C pointers.
But this explanation is too general and vague for me to understand the concept. I know type * means it is a pointer type, and this type stores pointers of that type. But why some types are declared with *, others are not?
The int type is not object. It is a C language “primitive data type”. You generally interact with primitive C data types directly. E.g.,
int i = 0; // it’s now `0`
i = 42; // it’s now `42`
The NSInteger is just an alias for another primitive data type, long. The NSRange is a struct (thus, also not an object), so the same rule applies. So, for basic interaction with these primitive data types, no * pointer reference is generally needed. (There actually are times you also deal with pointers to primitive data types, but that is beyond the scope of this question.)
But NSString, NSNumber, and NSArray, however, are objects, so you must use pointers when declaring them.
Note, you've included id in the latter list where * is not used:
id firstObject = #"foo";
Be very careful. This actually is a bit misleading, because firstObject actually is a pointer to the #"someString", a NSString object. The id is an exception to the way we generally declare pointers with * and is discussed in Objective-C is a Dynamic Language), “The id type defines a generic object pointer.”
It’s analogous to declaring a NSString pointer, but “you lose compile-time information about the object”. Compare the id pointer to the NSString * to the following syntax:
NSString *secondObject = #"bar";
This latter secondObject is NSString * pointer, but because we explicitly declared it as a NSString * pointer, the compiler knows that you are dealing with a string (thus the compiler can perform greater validation regarding your subsequent use of that pointer). With id, you do not enjoy this compile-time validation.
For example, here are two invalid attempts to call removeObject method on two NSString objects. NSString has no such method. The NSString * reference provides useful compile-time error message that id does not afford us:
Now, if you used the id type and tried to run that code, it would crash when it attempted to call removeObject on firstObject. But, where possible, it is much better to have the compiler tell us about the error during compile-time, rather than at run-time.
So the id pattern should be used sparingly, only when you really need Objective-C’s dynamic type behaviors.
In Dart, looking at the code below, does it 'pass by reference' for list and 'pass by value' for integers? If that's the case, what type of data will be passed by reference/value? If that isn't the case, what's the issue that causes such output?
void main() {
var foo = ['a','b'];
var bar = foo;
bar.add('c');
print(aoo); // [a, b, c]
print(bar); // [a, b, c]
var a = 3;
int b = a;
b += 2;
print(a); // 3
print(b); // 5
}
The question your asking can be answered by looking at the difference between a value and a reference type.
Dart like almost every other programming langue makes a distinction between the two. The reason for this is that you divide memory into the so called stack and the heap. The stack is fast but very limited so it cannot hold that much data. (By the way, if you have too much data stored in the stack you will get a Stack Overflow exception which is where the name of this site comes from ;) ). The heap on the other hand is slower but can hold nearly infinite data.
This is why you have value and reference types. The value types are all your primitive data types (in Dart all the data type that are written small like int, bool, double and so on). Their values are small enough to be stored directly in the stack. On the other hand you have all the other data types that may potentially be much bigger so they cannot be stored in the stack. This is why all the other so called reference types are basically stored in the heap and only an address or a reference is stored in the stack.
So when you are setting the reference type bar to foo you're essentially just copying the storage address from bar to foo. Therefore if you change the data stored under that reference it seems like your changing both values because both have the same reference. In contrast when you say b = a your not transferring the reference but the actual value instead so it is not effected if you make any changes to the original value.
I really hope I could help answering your question :)
In Dart, all type are reference types. All parameters are passed by value. The "value" of a reference type is its reference. (That's why it's possible to have two variables containing the "same object" - there is only one object, but both variables contain references to that object). You never ever make a copy of an object just by passing the reference around.
Dart does not have "pass by reference" where you pass a variable as an argument (so the called function can change the value bound to the variable, like C#'s ref parameters).
Dart does not have primitive types, at all. However (big caveat), numbers are always (pretending to be) canonicalized, so there is only ever one 1 object in the program. You can't create a different 1 object. In a way it acts similarly to other languages' primitive types, but it isn't one. You can use int as a type argument to List<int>, unlike in Java where you need to do List<Integer>, you can ask about the identity of an int like identical(1, 2), and you can call methods on integers like 1.hashCode.
If you want to clone or copy a list
var foo = ['a', 'b'];
var bar = [...foo];
bar.add('c');
print(bar); // [a, b, c]
print(foo); // [a, b]
var bar_two = []; //or init an empty list
bar_two.addAll([...bar]);
print(bar_two); // [a, b, c]
Reference link
Clone a List, Map or Set in Dart
I have saved value in Singletone as NSString. When I want to convert to int, value is some random number. For example, I am calling NSString *numberCoupons = [Manager sharedInstance].userProfile.numberOfCoupons, po numberCoupons returning normal value: 40.
But problem is in next line, when I want to convert string to value: int coupons = (int)numberCoupons; It is returning some random number, etc. 421414.
What could be the problem?
try int coupons = [numberCoupons integerValue];
numerofCoupons is obviously an NSNumber object which is used to store numbers within Objective-C collection classes (NSArray, NSDictionary, etc) as only objects can be stored in them.
To get the wrapped value out of the object use:
NSInteger coupons = [numberOfCoupons integerValue]
I would recommend redeclaring numberOfCoupons as NSInteger, and not NSNumber, as NSNumber objects are difficult and expensive to manipulate compared to the primitive types they wrap.
If the value needs to go into a collection class then wrap it in an NSNumber object when adding it and unwrap it when removing it.
When you write (int)numberOfCoupons you are asking that the value in the variable numberOfCoupons be cast to the type int.
Now the value in a variable of type NSString * is a reference to an object, that is a memory address. When (Objective-)C casts a reference to an integer type you get back the memory address. This is the “random” value you are seeing.
What you need to do is send a message to the object referenced by the value in your variable requesting that it return an integer value equivalent to itself. NSString has a method intValue for this, so [numberOfCoupons intValue] will do what you wish.
There is a whole family of xValue methods to obtain various integer and floating-point values of different precision/size.
Note: if you have a reference to an NSNumber, rather than an NSString, then exactly the same code will work.
Note 2: if you do have an NSNumber then the cast expression you first tried may return a value which has a completely different magnitude than you might expect for a memory address. This is because some integer values are represented by special tagged addresses which don't actually reference a real object. This is an optimisation you normally would not notice, except when you accidentally cast the reference value to an integer...
HTH
Consider below struct:
typedef struct _Index {
NSInteger category;
NSInteger item;
} Index;
If I use this struct as a property:
#property (nonatomic, assign) Index aIndex;
When I access it without any initialization right after a view controller alloc init, LLDB print it as:
(lldb) po vc.aIndex
(category = 0, item = 0)
(lldb) po &_aIndex
0x000000014e2bcf70
I am a little confused, the struct already has valid memory address, even before I want to allocate one. Does Objective-C initialize struct automatically? If it is a NSObject, I have to do alloc init to get a valid object, but for C struct, I get a valid struct even before I tried to initialize it.
Could somebody explains, and is it ok like this, not manually initializing it?
To answer the subquestion, why you cannot assign to a structure component returned from a getter:
(As a motivation this is, because I have read this Q several times.)
A. This has nothing to do with Cbjective-C. It is a behavior stated in the C standard. You can check it for simple C code:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B. No, it is not an improvement of the compiler. If it would be so, a warning would be the result, not an error. A compiler developer does not have the liberty to decide what an error is. (There are some cases, in which they have the liberty, but this are explicitly mentioned.)
C. The reason for this error is quite easy:
An assignment to the expression
NSMakeSize( 1.0, 2.0 ).width
would be legal, if that expression is a l-value. A . operator's result is an l-value, if the structure is an l-value:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.
ISO/IEC 9899:TC3, 6.5.2.3
Therefore it would be assignable, if the expression
NSMakeSize( 1.0, 2.0 )
is an l-value. It is not. The reason is a little bit more complex. To understand that you have to know the links between ., -> and &:
In contrast to ., -> always is an l-value.
A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)
Therefore - that is what footnote 83 explains – ->, &, and . has a link:
If you can calculate the address of a structure S having a component C with the & operator, the expression (&S)->C is equivalent to S.C. This requires that you can calculate the address of S. But you can never do that with a return value, even it is a simple integer …
int f(void)
{
return 1;
}
f()=5; // Error
… or a pointer …
int *f(void)
{
return NULL;
}
f()=NULL; // Error
You always get the same error: It is not assignable. Because it is a r-value. This is obvious, because it is not clear,
a) whether the way the compiler returns a value, esp. whether he does it in address space.
b) when the time the life time of the returned value is over
Going back to the structure that means that the return value is a r-value. Therefore the result of the . operator on that is a r-value. You are not allowed to assign a value to a r-value.
D. The solution
There is a solution to assign to a "returned structure". One might decide, whether it is good or not. Since -> always is an l-value, you can return a pointer to the structure. Dereferencing this pointer with the -> operator has always an l-value as result, so you can assign a value to it:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
You do not need #public for that. (What really is a bad idea.)
The semantics of the property are to copy the struct, so it doesn't need to be allocated and initialized like an Objective-C object would. It's given its own space like a primitive type is.
You will need to be careful updating it, as this won't work:
obj.aIndex.category = 1;
Instead you will need to do this:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
This is because the property getter will return a copy of the struct and not a reference to it (the first snippet is like the second snippet, without the last line that assigns the copy back to the object).
So you might be better off making it a first class object, depending on how it will be used.
I was looking at looping through an array and I stumbled across this method --
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
NSEnumerationOptions is defined as -
enum {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};
typedef NSUInteger NSEnumerationOptions;
The description for these are taken from NSHipster's blog post about enumeration --
NSEnumerationConcurrent: Specifies that the Block enumeration should
be concurrent. The order of invocation is nondeterministic and
undefined; this flag is a hint and may be ignored by the
implementation under some circumstances; the code of the Block must be
safe against concurrent invocation.
NSEnumerationReverse: Specifies
that the enumeration should be performed in reverse. This option is
available for NSArray and NSIndexSet classes; its behavior is
undefined for NSDictionary and NSSet classes, or when combined with
the NSEnumerationConcurrent flag.
NOW usually loops are written as -
for ( int i=0 ; i< count; i++ )
{
//stuff
}
My question is, why does NSEnumerationReverse exist and NSEnumerationForward does not. Why did Apple think that looping in reverse would be better than looping from 1st index.
Is there a performance benefit of looping through arrays in reverse? Or do i not understand NSEnumerationReverse properly?
The declared NSEnumerationOptions are for times when you want to use non-standard behaviour — forward iteration is the default behaviour that is automatically used unless you specify a different option.
To iterate forwards, simply pass 0 for the enumeration options parameter.
Note also that, since you're passing zero for the options anyway, you may as well just use the enumerateObjectsUsingBlock: method that does not accept any options, and does exactly the same thing otherwise (and is shorter to type).