In C, C++, C# you can have arrays of a certain type like int[].
In objC a NSArray can have a different type for every element.
If you want to have an NSArray of type x, you now can just treat the type of the element at index 0 as type of the array, meaning if [anArray objectAtIndex:0] is a NSString, you treat all objects as a NSString and throw an error, if any element is of a different type than the element at index 0. It is ugly, but you can simulate typed arrays that way.
Now, in C# it is even possible to have an empty array of type x with 0 elements.
However an empty NSArray of course has no element, not even at index 0, so there is no typeinfo in there.
Now if an objC app gets an array from a C# app or vice versa and the C# app in some situations sends or expects an empty array, the receiving side has to know the type of, how can get/set this typeinfo in objC?
Of course I could use C-arrays instead of NSArrays, but if you want to store a c-array in a NSDictionary, you have to put it into a NSValue first and a NSValue does not offer a possibility to store the size of a dynamically allocated c-array.
Is there any better option than to implement a new class "CArrayValue" for storing C_Arrays with typeinfo and size in a NSDictionary?
Make a wrapper for NSArray, one that contains a type key as instance variable and an NSArray. In that way you should be able to easily control the type of the array. Also what you could do to prevent adding types that are not allowed, is writing some methods that would remove / add items t o the array / initialize it.
Related
I need to store a bunch of objects in an NSOrderedSet object.
Unfortunately, I don't want the set to perform any sort of equality checking on anything other than the object pointer itself.
It appears as though some objects (such as NSNumber) override isEqual: and perform internal value comparison, which means that two NSNumber instances with the same value (but different object pointers) cannot be stored in the same ordered set.
How can I work around this issue?
It has occurred to me that I could just store the object pointer itself as an NSValue or NSString object (using #"%p") instead.
However this means that I need to wrap all calls to containsObject: and indexOfObject: and create a new NSValue or NSString object every time I want to query the set.
Is there any better way of handling this?
This question already has answers here:
Immutable/Mutable Collections in Swift
(7 answers)
Closed 5 years ago.
I don't have any problem, i would just like some clarification on an issue regarding mutability.
In Objective-C we would use for example NSMutableArray to get a mutable array and an NSArray to get an immutable one. I don't know much about the inner workings of the two, but from what I can remember I believe that the difference is that NSArray only reserves an amount of memory specific to the initial value which makes it more efficient, while NSMutableArray has no idea how much memory it will require. Presumably this means that NSMutableArray has pointers to bits of memory that are all over the place and not one by one like with NSArray? Or does it perhaps just reserve a lot of memory hoping it won't run out?
In Swift the obvious substitution is let for immutable and var for mutable. If a variable is declared with these keywords that I see no difference between Swift and Objective-C. However, I don't understand how it works if I declare the variable without the var/let by, for example, storing it in another variable.
Let's say I have a dictionary such as [String: [String]]. In other words, for each string key there is an array of strings. Consider the following case:
var dictionary: [String: [String]] = [:]
dictionary["key"] = ["string0", "string1"]
//dictionary now is ["key": ["string0", "string1"]]
But what is the strings array now? Is it mutable because the dictionary is mutable? Is it mutable because everything we assign is mutable? How about the following case:
let dictionary = ["key": ["string0", "string1"]]
dictionary["key"].append("string2")
Would this work?
I guess the key issue is that in Objective-C I always define whether am I working with NSMutableArray or NSArray. Creating an array using the literal syntax [#"string"] always leads to an NSArray and it won't be mutable unless I specify it. In Swift I don't know when is what mutable.
Thanks
For arrays and dictionaries, the let or var keyword decides whether the whole collection would be mutable or immutable. In other words, if you declare a dictionary immutable by using the let keyword, you cannot change any of its values, so the second example would not work.
In Swift deciding whether a collection will be mutable or immutable only depends on the keyword you use to declare it, so declaring an array/dictionary using the let keyword will be equivalent to declaring an immutable array (NSArray in Objective-C) while declaring it with the var keyword will give you a mutable array (NSMutableArray in Objective-C).
If you create an array, a set, or a dictionary, and assign it to a
variable, the collection that is created will be mutable. This means
that you can change (or mutate) the collection after it is created by
adding, removing, or changing items in the collection. If you assign
an array, a set, or a dictionary to a constant, that collection is
immutable, and its size and contents cannot be changed.
So, let means constant, if you declare array or dictionary using let it will be immutable.
if you declare array or dictionary as var it will be mutable.
So, the case below will not work because dictionary will be immutable:
let dictionary = ["key": ["string0", "string1"]]
dictionary["key"].append("string2")
Check the reference here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html
So I am busy reading an objective-c book by Big Nerd Ranch. I'm on chapter 17 at the moment and managed to complete the required challenge at the end of the chapter. However, I just have two question that I would like to understand.
In the following bit of code - StockHolding is a custom class that has instance variables and the stocks (an array) points to three instances of stockholding with values setting its stock value and cost in dollars.
At first I tried to access the array to get the data from the objects it pointed to - but it seems that was not going to work as the array doesn't know what data its objects contain - just where they are in memory, right?
What I want to know is why was it necessary to create a new instance of stockholding (holdings) in this for loop to access those variables?
How does the new instance of stockholding know what the values of my stocks are?
for (StockHolding *holdings in stocks){
NSLog (# "%# has %d shares. Cost: $%.2f. Stock value: $%.2f", [holdings stockName],[holdings numberOfShares], [holdings costInDollars], [holdings valueInDollars]);
}
I'm going to try have a guess here to see if maybe I understand it a little better?
We create an instance of our class in the for loop so that we have access to its instance methods and variables - then we use the stocks array to get the variables from those objects in the array?
I may be completely off.. :(
Any advice?
stocks is an array having the objects of type StockHolding
So in order to access all values in the array and print the values.You need to get all the StockHolding instance inside the array we use for ...in method
note Here new instance is not created just new reference is made to the memory that is in the array so that you can access it and use it
Absolutely no new instances are created in the for loop at all. Since Objective-C objects are always represented as pointers, one variable != one instance. The holdings local variable inside the loop is assigned the pointer to the element of the array which is currently being enumerated upon each iteration. It's just a "reference" to an already existing object.
You're not creating new instances. You're iterating through existing instances.
Presumably in [CODE] you have created the objects and added them to the NSArray. The for loop just gives them to you one at a time. You name it holdings, do something with it, then grab the next.
That's all.
In Objective-C objects are typeless. Any message can be sent to any object. Code like [holdings stockName] means "send the message 'stockName' to the object 'holdings'". So the Objective-C runtime will inspect the object to see whether it implements that message. If so then it'll pass execution into the implementation.
The type of your object makes no difference to how processing will occur at runtime.
An NSArray stores anything that conforms to the NSObject protocol. So it can hold any old mix of objects. The same goes for the other collections.
Although you could write all your code without mentioning a single object type, you usually don't because if you say which type of objects you're dealing with then the compiler can perform some sanity checks. That makes you less likely to write broken code.
So the code:
for (StockHolding *holdings in stocks)
just means "let me do something to every object in the collection stocks and don't give me any compiler warnings when I treat them like instances of StockHolding". They may actually be other classes. If they're other classes that implement stockName, numberOfShares and the rest then your code will work perfectly.
So, for example:
NSMutableArray *arrayOfStrings = [NSMutableArray array];
[arrayOfStrings addObject:#"34.3"];
[arrayOfStrings addObject:#"19.8"];
float total;
for(NSNumber *number in arrayOfStrings)
{
total += [number floatValue];
}
Will compile and work perfectly — not because the strings are actually converted to numbers but because both classes implement floatValue to return a float. So each NSNumber *number is actually an NSString, and if you tried to call, say, isEqualToNumber: on any of them you'd raise an exception because that isn't implemented by strings. But telling the compiler you're going to act as if they're numbers means you don't get a warning for using floatValue and when the runtime spots that the object implements floatValue execution continues as usual.
The for..in loop is used for fast enumeration.
This
for (StockHolding *holdings in stocks)
{
}
won't create any new object, it takes one object from array and cast it to the specified type and assign it to the specified variable.
Means:
Takes the object from the array . Equivalent to [stocks objectAtIndex:index];
Assign it to the specified object. Equivalent to StockHolding *holdings = [stocks objectAtIndex:index];
Note that Only the reference is used (assignment) there is no object is allocated.
How do you fill a NSMutableArray with a set capacity for later use?
Basically I want to set up a NSMutableArray to act as a map for my game objects, so I have this line...
gameObjects = [[NSMutableArray alloc] initWithCapacity:mapWidth*mapHeight];
Which I had hoped would create and fill my MutableArray so I can get then access it with this kind of index...
int ii = (cellY*mapWidth)+cellX;
NSDictionary *currentObject = [gameObjects objectAtIndex:ii];
But I just learned initWithCapacity doesn't fill the array, so should I create blank objects to fill it with, or is there a Null that I can fill it with? Also would I do that with 2 for loops or is there an instruction something like "initWith:myObject" ?
I want to be able to check at a certain index within the array to see if there's an object there or not, so I need to be able to acces that index point, and I can only do that if there's something there or I get an out of bounds error.
I'll be using this NSMutableArray pretty much as a grid of objects, it's a 1 dimensional array organised as a 2 dimensional array, so I need to be able to fill it with mapWidth*mapHeight of something, and then calculate the index and do a check on that index within the array.
I've looked on here and googled but couldn't find anything like what I'm asking.
Thanks for any advice.
I think what you are looking for is [NSNull null]. It is exactly what you want- a placeholder value.
You can find more information on the topic in this question.
initWithCapacity is just a performance optimization -- it has no effect on the array behavior, it just keeps the code "under the covers" from having to repeatedly enlarge the internal array as you add more entries.
So if you want a "pre-allocated" array, you'd need to fill it with NSNull objects or some such. You can then use isKindOfClass to tell if the object is the right type, or simply == compare the entry to [NSNull null]. (Since there's only ever one NSNull object it always has the same address).
(Or you could use a C-style array of pointers with nil values for empty slots.)
But you might be better off using an NSMutableDictionary instead -- no need to pre-fill, and if the element isn't there you get a nil pointer back. For keys use a NSNumber object that corresponds to what would have been your array index.
initWithCapacity only hints to NSMutableArray that it should support this many objects. It won't actually have any objects in it until you add them. Besides, every entry in the array is a pointer to an object, not a struct like you'd normally have in a standard c array.
You need to change how you're thinking about the problem. If you don't add an object to the array, it's not in there. So either you pre-fill the array with "empty" objects as you've said, which is weird. Or you can add the objects as you need them.
I'm trying to save pointers of class instances into a mutable array. I'm able to do this but getting them back into use seems to be a problem. The next is how I inserted the pointers into the array:
Class *class = [Class new];
/* Do something with the instance */
[instanceArray addObject:class];
I am able to retrieve the wanted index from the array but when I try to access the instance variables etc. I only get compiler shouting at me or sometimes I get every variable as zero.
How am I supposed to get the instance back to use from the array? I know they are pointers but playing with them didn't seem to work.
Using addObject: the element is inserted at the end of the array. To retrieve it you can use -[NSArray objectAtIndex:]
Pointer arithmetic works in C since then your array is just a pointer to the first index, and array[i] is the same as *(array + i). In Objective-C this may still be done, however you're using an NSArray object. Now you don't have a pointer to the first object anymore, but to the instance of the class. To retrieve the objects stored in the array, you'll have to call the corresponding methods.
EDIT
So you are able to retrieve it from the array and then your pointer is not nil, so you do have access to the object. You know in Objective-C, all instance variables are private so you can't access them from outside. You'd have to declare them as properties first (please refer to the documentation). Also, when you declare a variable to be of type id, accessing a property with dot-syntax doesn't work, it will cause a compiler error.