I am hoping not to need to use an NSMutableArray here. I have an array with 10 elements. I want to change the value at index 4. Is there a way to do this without having to use NSMutableArray? The problem with NSMutableArray is that we can change anything about it, including its size. I don't want the size of this array to change accidentally. I just want to change the value at index 4 from say 22 to 25. How might I do that? doing array[4]=25 is not working.
NSArray *ar1 = #[#"1",#"2"];
NSMutableArray *ar1update = [ar1 mutableCopy];
ar1update[1] = #"Changed";
ar1 = [NSArray arrayWithArray:ar1update];
The only way is to create a new NSArray and change your pointer to a new NSArray. I can give an example...
In interface:
#property (strong, nonatomic) NSArray *myArray;
In implementation:
- (void) updateMyArray{
NSMutableArray *myArrayMut = [self.myArray mutableCopy];
myArrayMut[4] = #"new item";
self.myArray = [myArrayMut copy];
}
So basically, you can create a mutable copy temporarily, make the change you need, and then make an immutable copy. Once you have the immutable copy, you can point myArray to the new copy. As long as you are only changing existing items in updateMyArray and the myArray starts out with 10 items or less, you will never be able to have more than 10 items.
If you don't wish to use NSMutableArray how about a plain old C array? E.g.:
int array[10];
...
array[4] = 25;
You can store Objective-C objects in such an array and ARC will handle the memory management.
If you really want a fixed-sized NSArray/NSMutableArray you can do that by subclassing those types yourself - subclassing NSArray only requires implementing two methods and you can use an underlying C array or NSMutableArray for the actual storage.
HTH
Related
I have an NSMutableArray 'myArray' which contained some customObject 'A', Now I want to convert 'myArray' to tempArray which contained some object which subclass 'A' named as 'B'.
As a property just like follow:
NSMutableArray <B *> tempArray;
tempArray = [myArray mutableCopy];
But the object in tempArray always is kind of 'A'. I want convert 'A' to 'B', Any suggestion?
'B' has more property which i want to use.
How do you expect this to work? B is a child of A, it contains more information (the property you wish to use etc.). Where would the data for that property come from?
You need a way to construct a new instance of B using (the properties of) an instance of A and supplying the extra data a B needs. B might already have a suitable init method, or you may need to write your own code.
Once you have a way to produce a new B you can just iterate over your array building a new one, building a new B instance for each A instance.
HTH
If you have an immutable array (NSArray) that means you can't add or remove objects, or replace objects with other objects. [myArray mutableCopy] creates an NSMutableArray. It will contain exactly the same objects as the immutable array, but you are now free to add or remove or replace objects.
Your declaration NSMutableArray * tempArray does nothing but lie to the compiler. If myArray contained objects of type A*, then tempArray contains the same objects of type A*. You are just lying, so the compiler believes they are objects of type B*, but they are not.
There is no way on earth how a compiler could automatically convert an object of some class to an object of a subclass. If the subclass has additional members, for example, how is the compiler going to fill those members? This just cannot work.
If you want to create new objects of type B*, based on objects of type A*, you have to do that by hand, likely using an initialiser like
- (instancetype)initWithA:(A*)a;
in your B interface. Your code could be for example
NSMutableArray <B*> *tempArray = [myArray mutableCopy];
for (NSUInteger i = 0; i < tempArray.count; ++i) {
tempArray [i] = [[B alloc] initWithA:tempArray [i]];
}
or possibly
NSMutableArray <B*> *tempArray = [[NSMutableArray alloc] init];
for (A* a in myArray)
[tempArray addObject:[[B alloc] initWithA:a]];
I have an array - placeObjectsArray, that hold a lot of objects called place. Place is object of class PlaceHolder, in which i create different properties, filled with data:
self.place = [[PlaceHolder alloc]init];
// A lot of code here during parson XML with data
[self.placeObjectsArray addObject:self.place];
Header of this file look like this:
#interface PlaceHolder : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *webPage;
It actually a container for an entity, each one hold data for name, description, image links etc. At now, i have array with place objects. What i want to, to manipulate with that objects inside an array. For example, how could i find all of data for specific "name"? (Name is one of properties in PlaceHolder class). How could i make an array that contain only names? How could i see in console 10 random "description"?
Any advice would be appreciated, thanks!
You're asking a bunch of separate questions.
First, how to select items in your array that match a particular name: Create an NSPredicate and use filteredArrayUsingPredicate. Google NSPredicate and you should find lots of examples.
Alternately you could use indexesOfObjectsPassingTest to get an index set of the items in the array that match your search criteria, and then use objectsAtIndexes: to turn the index set into a sub-array.
As for how to get all the names from the entries in your array, you can use a very cool trick in key value coding.
If you send an array the valueForKey message, it tries to fetch an item from each entry in the array using that key, and return them all in a new array. The code would look like this:
NSArray *names = [placeObjectsArray valueForKey #"name"];
Fetching 10 random descriptions is a little more complicated. You would need to write code that loops through the array, selecting 10 random items, and appends the description of each one into a new mutable array.
The trick there is to use arc4random_uniform to get a random index in your array:
NSUInteger random_index = arc4random_uniform(placeObjectsArray.count);
I leave the rest to you as a learning exercise.
If you want to fetch 10 random descriptions and make sure you never fetch the same description twice it's more complicated. You need to create a mutable copy of your array, then loop through the copy, fetching a random item, adding it's description to an array, and deleting the item from the array.
You can use NSPredicates:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name LIKE[cd] %#", nameSearch];
NSArray *filtered = [self.placeObjectsArray filteredArrayUsingPredicate:predicate];
You could iterate over your array looking for the PlaceHolder with a given name, like:
PlaceHolder *namedPlaceholder = nil;
for (PlaceHolder *placeholder in theArray) {
if ([placeholder.name isEqualToString:"whateverName"]) {
namedPlaceholder = placeholder;
break;
}
}
If you want to find PlaceHolders by name efficiently you might consider using a dictionary instead of an array. With a dictionary you can map names to objects, like:
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
myDictionary[#"foo"] = somePlaceholder;
myDictionary[#"bar"] = someOtherPlaceholder;
and retrieve them like:
PlaceHolder *somePlaceholder = myDictionary[#"foo"];
To get random objects from an array, I recommend getting random indexes using arc4random_uniform. This gives pseudo-random numbers with a better uniform distribution than rand or random, and does not require you to explicitly seed the sequence with srand or srandom.
PlaceHolder *randomPlaceholder = theArray[arc4random_uniform(theArray.count)];
or
const NSUInteger arrayCount = theArray.count;
for (NSUInteger j = 0; j < 10; j++) {
PlaceHolder *randomPlaceholder = theArray[arc4random_uniform(arrayCount)];
// Do something with randomPlaceholder.
}
I have a NSMutableArray that i define in the header file as:
#property (strong, nonatomic) NSMutableArray *tempPhotosArray;
Then i allocate as:
_tempPhotosArray = [[NSMutableArray alloc] init];
What i'd like to know is if i then go to replaceObjectAtIndex the program will complain on an out of bounds. I want to keep only a set number of items in that array, so is it possible to do a insert or replace? i.e. if at index 0 it is empty do an insert, if there is an object already replace it?
Thanks
i think i agree with Hani Ibrahim. Since you said you only want to keep a set number of objects in the array. So how many you want?
// add these code when you initialize the array
int aSetNumber = 5;
_tempPhotosArray = [[NSMutableArray alloc] init];
for (int i = 0; i < aSetNumber; i++)
{
[_tempPhotosArray addobject: [NSNull null]];
}
i guess then you can do whatever you want, i don't know what exactly you want to do in this case, but i would check if the object in that position is NSNUll, if so, replace that, if not, i don't know what you want them
//use these code when you trying to insert the real object
if([[_tempPhotoArray objectAtIndex:anIndex] isKindOfClass: [NSNull class]])
{
//replace it here
}
As to why you are getting an error, what everyone else wrote is accurate, but....
The description of what you want doesn't match what an NSArray is. It sounds like you want a list of up to 5 items and never more than 5. It might be that if you try to add a 6th item the "oldest" goes away. Like a "recently opened" file history. You can make this type of functionality with an NSArray, but that's not what it is out of the box.
I would suggest making your own object class. I'm not going to write all the code for you, because this sounds suspiciously like programming homework, but I will point you in the correct direction.
FivePack <-- our class
NSArray *storage; <-- where we house the data
// a public method which lets you add things.
- (void)addItem:(id)item {
int indexOfLastItemInArrayToSave = 4;
if (storage.length < 4)
indexOfLastItemInArrayToSave = length-1;
NSRange range = NSMakeRange(0, indexOfLastItemInArrayToSave);
NSArray *temp = [storage subArrayWithRange:range];
// now create a new array with the first item being "item" that
// was passed in and the rest of the array being the contents of temp.
// Then save that to storage.
}
What you want to do with the data and writing something to get it from your new object is up to you, because I'm not sure how you want to do it.
There are no objects in the array when you initially created it, so there is nothing to replace.
Like this?
if([_tempPhotosArray count] > 0)
//replace object
else
//add object to array
I'm trying the following code to create an instance, assign properties, add to array.
Then, assigning new properties and adding again.
However array will contain 2 identical objects (equal to the second one added). The class Message simply has several (nonatomic, retain) NSStrings/Integer properties.
This probably has something to do with my understanding of pointer, can someone explain?
self.messages=[[NSMutableArray alloc]init];
Message *m=[[Message alloc]init];
m.cb=#"2402";
m.ck=1001;
m.msg=#"as";
[self.messages addObject:m];
m.cb=#"2422";
m.ck=1002;
m.msg=#"aadfsdsdfdssdklsdflkh";
[self.messages addObject:m];
NSLog(#"%#",self.messages);
When you add an object to an array, it does not add a copy of the object to the array, but instead just a reference to it. If you want two different objects, then you need to create two different objects instead of re-using the same one (or, as #Brendon points out, create a copy when you add it to your array).
To fix your example, the most common technique would be to add the following line right before you start modifying the properties for the second object:
m=[[Message alloc]init];
Or, use a second pointer and object instead of reusing m.
EDIT:
To add a copy, change [self.messages addObject:m]; to [self.messages addObject:[m copy]];, assuming that the Message class conforms to the NSCopying protocol.
Yes, after executing the posted code self.messages contains the Message object twice, at indexes 0 and 1. That's not a problem, though. Arrays can contain any object, even themselves.
It seems that you want two distict objects, so you would just create a second Message.
You can either implement the NSCopy protocol — as mentioned by lnafziger — or just create new instances quite easily in a for loop.
«Two or more, use a for»
— Edsger W. Dijkstra
self.messages=[[NSMutableArray alloc] init];
NSArray *dataArray = #[ #{#"cb": #"2402", #"ck": #(1001), #"msg": #"as"},
#{#"cb": #"2422", #"ck": #(1002), #"msg": #"aadfsdsdfdssdklsdflkh"}
];
for(NSDictionary *data in dataArray) {
Message *m=[[Message alloc] init];
m.cb = data[#"cb"];
m.ck = [data[#"ck"] integerValue];
m.msg = data[#"msg"];
[self.messages addObject:m];
}
I have a model class which contains an NSMutable array of objects. The controller classes need to have access to this array, however that access should be read only.
How should this be implemented? Should the model expose the array as a (readonly) NSMutable array and use consts, or expose it as an NSArray? If the latter how can the NSArray be created efficiently from the NSMutableArray i.e. how should the NSArray contain a reference to the NSMutableArray/its contents rather than have duplicate copies? (the NSMutableArray is guaranteed to persist in memory while the controllers access it).
You can just return your NSMutableArray directly:
- (NSArray *)method
{
return myMutableArray;
}
NSMutableArray is a subclass of NSArray, so the controllers will be able to perform all of the NSArray operations on it already. If you're really concerned that somebody might be trying to pull tricks on you, you could use:
return [NSArray arrayWithArray:myMutableArray];
to return an immutable copy.
There is no read only NSMutableArray. If you are thinking that defining a property as read only makes the returned object immutable, this is not the case.
Expose a read-only property of type NSArray, and in the accessor return a new array as follows:
return [NSArray arrayWithArray:mutableArray];
Note that the objects within the array, if mutable, will still be changeable.
If you want to continue passing your mutable array, but want to ensure that the other method does not overwrite your existing array, you could always pass a copy
return [myMutableArray copy];
This way, you are returning a type NSArray (since NSMutableArray is a subclass) and ensure that myMutableArray itself is not getting tampered with.
NSMutableArray inherits from NSArray, so there is nothing to be done to expose NSMutableArray as an NSArray except to (probably implicitly) cast it. Note that it's still the same object, so if someone wants to be clever, they can downcast it or even just try to call NSMutableArray methods on it.
While this might be overkill, you could create your own custom class that has a #private ivar of an array, and then you could create readonly properties/methods to access data only.
i.e (implementation snippet):
#interface CustomArray : NSObject
{
#private
NSArray *array;
}
#end
- (id)initWithNSArray:(NSMutableArray *)array;
- (id)getObjectAtIndex:(int)index;
#implementation
// implement methods here
#end