NSSet to NSArray casting calling objectAtIndex? - objective-c

I'm trying to update an MKMapView by removing all annotations outside the visible area, and adding and removing some annotations inside the visible area. This is my code:
NSSet *visibleAnnotations = [mapView annotationsInMapRect:[mapView visibleMapRect]];
NSSet *allAnnotations = [NSSet setWithArray:[mapView annotations]];
NSMutableSet *nonVisibleAnnotations = [NSMutableSet setWithSet:allAnnotations];
[nonVisibleAnnotations minusSet:visibleAnnotations];
[mapView removeAnnotations:(NSArray *)nonVisibleAnnotations];
NSMutableSet *newAnnotations = [NSMutableSet setWithArray:[_zoomLevels objectAtIndex:clusterLevel]];
[newAnnotations minusSet:visibleAnnotations];
[mapView addAnnotations:(NSArray *)newAnnotations];
This gives me the error -[__NSCFSet objectAtIndex:]: unrecognized selector sent to instance 0x13cd40 after the final line in which I cast newAnnotations to an NSArray then add the annotations. Is there something about casting an array to a set that causes this? If so, is there a way round it?

Despite you're casting NSMutableSet to NSArray, that simple casting won't make NSSet class respond to NSArray's messages. You have to fill an actual NSArray with the elements of the NSSet like this:
NSArray *array = [theNsSet allObjects];

Casting an NSSet object to NSArray will not do anything else that tricking the compiler into thinking that the object is an NSArray. Actually, the object is an NSSet object and trying to use it as an NSArray will produce failure.
Another way to see it is that casting is just a trick on pointers, not on the pointed-to objects, that remain unaltered.
Casting is only safe in certain cases, like when you cast from a derived class to a base class; or when you are absolutely sure that the underlying object real type is consistent with the type you are casting it to.
Anyway, in your specific case, you might try to access the NSSet elements through an NSArray by using:
[newAnnotations allObjects]
This
Returns an array containing the set’s members, or an empty array if the set has no members.

Another way to get an NSMutableArray from an NSSet is
NSMutableArray * array= [[set allObjects] mutableCopy];
Also, the solution proposed by satzkmr gives you an "Incompatible pointer" warning. (Sorry to write this here, I don't have enough reputation to comment).

Yes, you should first store the set into an array like this...
NSMutableArray *array = [NSMutableArray arrayWithArray:[set allObjects]];

Following Step to be followed to convert a NSSet into NSArray or NSMutableArray,
NSSet *airports = [NSSet setWithObjects:#"Chennai",#"Mumbai",#"Delhi", nil];
NSLog(#"Set Elemets Before Move:%#", airports);
NSMutableArray *movedAirports = [airports allObjects];
NSLog(#"Array Elements After Moving from Set:%#", movedAirports);

NSArray *array = [sets allObjets];

Related

Why mutable object(NSMutableArray and NSMutableDictionary) required alloc- init?while we can directly assign value to immutable objects

//Mutable object...
//first initialize
NSMutablearray *arr=[[NSMutableArray alloc]init];
//then add value
[arr addobjects:#"iPhone",#"Android",nil];
//But we can assign value immutable array without initialize
NSArray *brr=#[#"iPhone",#"Android"];
There are many ways to initialize NSMutableArray. For example:
NSMutableArray *arr = [NSMutableArray arrayWithObjects: #"iPhone", #"Android", nil];
The #[] literal syntax is a fairly new addition to ObjC, and it returns an NSArray (non-mutable), which is why you can't assign it directly to an NSMutableArray. It's just syntactic sugar for [NSArray arrayWithObjects:count:]. If you want to use that syntax, you still can, though:
NSArray *brr = [#[#"iPhone",#"Android"] mutableCopy];
That's just the way the literal syntax is. In Objective-C, it creates an NSArray. If you really want to use the syntax but end up with a mutable array you can use a copy like this:
NSMutableArray *brr=#[#"iPhone",#"Android"].mutableCopy;
This system is much better in Swift, where it's just one value type called Array and you set mutability with either a var or a let
var myArray = ["iPhone", "Android"] // mutable
let myArray = ["iPhone", "Android"] // immutable
You object to the two-step process:
//first initialize
NSMutableArray *arr=[[NSMutableArray alloc]init];
//then add value
[arr addobjects:#"iPhone",#"Android",nil];
And you say you would rather write a literal array:
NSArray *brr=#[#"iPhone",#"Android"];
So combine them:
NSMutableArray* arr = [NSMutableArray arrayWithArray: #[#"iPhone",#"Android"]];
Because the literal's type is immutable. Also, you should remember NSMutableArray is a subclass of NSArray. You can assign an instance of subclass to its superclass, but not otherwise.

NSMutableArray with a specific class of object to be contained

Is there any way to specify what specific type of object can be contained within an NSMutableArray?
EDIT More specifically... Is there a way to restrict the class that the object must belong to?
Well, you could always subclass NSMutableArray but as everyone else has said, it is hard to imagine a good reason to do this....
From Subclassing Notes in the docs you would basically have to over-ride the following functions and check for the proper class:
insertObject:atIndex:
addObject:
replaceObjectAtIndex:withObject:
the primitive methods of the NSArray class
You could check the class of the object before adding it to the array.
NSMutableArray *myArray = [NSMutableArray alloc] init];
if ([someObject isKindOfClass:[ClassYouWantInArray class]]){
[myArray addObject:someObject];
}

Can the NSMultableArray mention which object inside the NSMultableArray?

The NSMutableArray can store every NSObject, but can I mention the NSMutableArray can get store my item only, for example, a NSMutableArray that store NSString only?
I remember that the java array can do that, can the objective C array do the similar things? Thanks.
Objective-C does not have this kind of generic constraint on NSArray/NSMutableArray. You have therefore two solutions:
Subclass NSArray/NSMutableArray and check for element type. It is strongly discouraged as NSArray/NSMutableArray is a class "cluster" and not obvious to subclass.
Create a category with specific methods that check the right type. You will have a compile-time enforcement of the type.
You can try it like this -
NSMutableArray *arr = [[[NSMutableArray alloc] init] autorelease];
if([obj isKindOfClass:[NSString class]])
[arr addObject:obj];
This way you end up adding only NSString to your arr.
Not by default, no. NSArray and its mutable counterpart just store pointers which happen to point obj-c objects. These objects can of any type. It would be up to you to make sure that only NSString's get in your array.
You could potentially subclass NSArray and override the addObject: methods such that they throw an exception if you try to add a non-NSString object.

How to convert NSArray to NSMutableArray

ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray *tempPeoples=[[NSMutableArray alloc]init];
for(int i=0;i<nPeople;i++){
ABRecordRef i1=CFArrayGetValueAtIndex(allPeople, i);
[tempPeoples addObject:i1];
// [peoples addObject:i1];
}// end of the for loop
peoples=[tempPeoples copy];
This code gives exception b/c I want to convert NSMutableArray to NSArray
Please Help
The subject reads, "How to convert NSArray to NSMutableArray". To get an NSMutableArray from an NSArray, use the class method on NSMutableArray +arrayWithArray:.
Your code does not show the declaration for peoples. Assuming it's declared as an NSMutableArray, you can run into problems if you try to treat it as such. When you send the copy message to an NSMutableArray, you get an immutable object, NSArray, so if you try to add an object to a copied NSMutableArray, you will get an error.
CFArrayRef is toll free bridged to NSArray, so you could simplify your code this way:
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
//NSMutableArray *tempPeoples = [NSMutableArray arrayWithArray:(NSArray*)allPeople];
// even better use the NSMutableCopying protocol on NSArray
NSMutableArray *tempPeoples = [(NSArray*)allPeople mutableCopy];
CFRelease(allPeople);
return tempPeoples; // or whatever is appropriate to your code
In the above code tempPeoples is an autoreleased NSMutableArray ready for you to add or remove objects as needed.
This code gives exception b/c I want to convert NSMutableArray to NSArray
This is very unlikely. NSMutableArray is a derived class of NSArray, so copying in that direction isn't an issue.
Maybe you've got an error because you don't retain the array. arrayWithArray returns an autorelease object. Either use [tempPeoples copy] or [[NSArray alloc] initWithArray: tempPeoples];
Simply you can do that
NSArray *yourArray ; // Static Array
NSMutableArray* subArrayData = [yourArray mutableCopy];

How to append two NSMutableArray's in Iphone sdk or append an NSArray With NSMutableArray?

I need to append two NSMUtableArray's can any one suggest me how it possible?
My code is:
NSMutableArray *array1 = [appDelegate getTextList:1];
NSArray *array2 = [appDelegate getTextList:2];
[array1 addObjectsFromArray:array2];//I am getting exception here.
Anyone's help will be much appreciated.
Thanks all,
Lakshmi.
What's probably happening, is that your [appDelegate getTestList:1] is not actually returning a NSMutableArray, but a NSArray. Just typecasting the array as mutable by holding a pointer to it like that will not work in that case, instead use:
NSMutableArray *array1 = [[appDelegate getTextList:1] mutableCopy];
NSArray *array2 = [appDelegate getTextList:2];
[array1 addObjectsFromArray:array2];
Or you could store the 'textList' variable that you have in your appDelegate as an NSMutableArray in the first place. I am assuming that you have an NSArray of NSArrays (or their mutable versions). Eg.
// In the class interface
NSMutableArray *textLists;
// In the function in which you add lists to the array
NSMutableArray *newTextList;
[self populateArray:newTextList]; // Or something like that
[textLists addObject:newTextList];
Note: that you will probably have a different workflow, but I hope that you get the idea of storing the actual lists as NSMutableArrays.
Another Note: the second method WILL modify in place the NSMutableArray that [appDelegate getTextList:1]; returns
Try this:
NSMutableArray *result =
[[appDelegate getTextList:1] mutableCopy]
addObjectsFromArray:[appDelegate getTextList:2]];
You're getting the exception because you're trying to send mutating messages to an immutable array.