Objective C: How do I make an NSMutableSet of NSMutableDictionaries, and have them be unique based on one of the dictionary values? - iphone

The reason I'm asking this is because right now I use an NSMutableSet of integers and a corresponding NSMutableArray to store the rest of the data, but I think I'm not using the language right. Here's my code:
In the init function:
self.markerList = [[NSMutableArray alloc] init];
self.markerIdSet = [[NSMutableSet alloc] init];
And the function that updates the set and array:
- (void) addMarker: (RMMarker*)marker AtLatLong:(CLLocationCoordinate2D)point {
if (![self.markerIdSet member: [node objectForKey:#"id"]]) {
[self.markerIdSet addObject:[node objectForKey:#"id"]];
[self.markerList addObject:marker];
}
}

I would be a bit wary of having a mutable object as part of a key in a set as if the object changes its hash code etc could change but the set would not know about it ( unless you make the addMarker remve the object and then add it back with the new hash code)
However in your example can't you just use a dictionary (or am I missing something?)
self.markerDict = [[NSMutableDictionary alloc]init];
- (void) addMarker: (RMMarker*)marker AtLatLong:(CLLocationCoordinate2D)point {
if ([nil != [self.markerDict [node objectForKey:#"id"]]) {
[self.markerDict setValue:marker forKey:[node objectForKey:#"id"]];
}
}

The NSMutableSet contains unique elements. Uniqueness is determined by the hash and isEquals methods, so in order to do what you want, you should either subclass or delegate an NSMutableDictionary and in your own concrete class implement these methods so that the specific key you require is the equality and hash factor.

Don't make an NSMutableSet of NSMutableDictionary directly; make a custom class that has an NSMutableDictionary property. This custom class should implement -isEqual: and -hash in terms of your id—i.e. this object will be equal to another object if the ids are equal, and two objects with the same id will have the same hash.
More documentation with regard to -isEqual: and -hash.

Related

creating an object out of multiple variable types

I have an NSDictionary that I am passing to a NSObject Class where I pull all of the values out of the dictionary and pass them into their correct types.
For instance NSInteger, BOOL, NSString, char are the types of values I am pulling out of the NSDictionary and putting into their only variables.
My question what is the best way to turn these values into one big object that can then be putt into an array?
I have heard that I can use the class itself as an Object.. But I am not really sure how to do this.
or could I just put them back into a NSDictionary?... but if thats the case do NSDictionaries allow for multiple value types?
Actually, you are in the right path. this is basically MVC architecture way. so you are questioning about M = Model.
Model in here example is class that defines all variables. cut to the point, here's you should do:
-> create a class that contain your variable, with #property & #synthesize name : ClassA.
then you could set object ClassA into dictionary.
ClassA *myClass = [[ClassA alloc] init];
myClass.myString = #"test String";
myClass.myBoolean = True;
[dictionary setObject:myClass forKey:#"myObject"];
[myClass release]; //we no longer need the object because already retain in dictionary.
then retrieve it by :
ClassA *myClass = (ClassA*)[dictionary objectForKey:#"myObject"];
NSLog(#"this is value of myString : %# & boolean : %i",myClass.myString,myClass.myBoolean);
You can put the collection of values in NSDictionary, NSArray, NSMutableArray, etc. Any of the collection types. Or if you have a fixed number of values/types, you can create a class that has a data member for each value/type and put it in that. The class solution would eliminate having to do a lot of casting, but it also only works if there are a fixed number of values/types.
Here is the AppleDoc on collections.
Yeah, You can create a class itself with these as different values as a properties.Once you pass this object to any class you can access those values there by obj.propertyName.Doing by this Lead to create Modal in MVC pattern.
Please let me know if there is doubt.
Test *object = [[Test alloc] init];
object.testBool=true;
object.testString=#"Test";
NSDictionary *passData = [[NSDictionary alloc] initWithObjectsAndKeys:object,#"testObject", nil];
Test *getObject = (Test*)[passData objectForKey:#"testObject"];
NSLog(#"%d",getObject.testBool);
NSLog(#"%#",getObject.testString);
You can customized the init method of Test Class.

I have a Objective C function that takes any type of object by reference. But when i pass a NSMutableArray My function does not recognise It

I have a function That takes by reference any kind of object
-(BOOL)RemoteCall:(id**)DataClass;
in the implementation i use [*DataClass isMemberOfClass:[NSMutableArray class] to find out the type of the object. The problem is it does not work with NSMUtableArrays Does anybody have a solution to this problem ? Here is the relevant code:
Implementation:
-(BOOL)RemoteCall:(id**)DataClass
{
if([*DataClass isMemberOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)*DataClass;
//do something with SW
DataClass= (id**)SW;
return TRUE;
}
}
Any help and I mean anything at all will be appreciated, I'm stuck.
Method Call:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]
Services * serv = [[Services alloc] init];
return [serv RemoteCall:&channelArray];
Pass by reference in Objective-C is almost never the right way.
There are a number of problems with that code.
(id**) is a pointer to a pointer to a pointer to an object. Probably not at all what you want.
YES and NO are BOOL return types; not TRUE
there is no reason in that code to be returning something by reference.
method names start with lower case letters. Arguments do, too.
There will never be an instance of NSMutableArray in an application; just subclasses
You can't tell the difference between a mutable and immutable array in the first place; check for isKindOfClass: or isMemberOfClass: for an NSMutableArray won't do you much good (it is useful, but misleading).
This is better:
-(BOOL)remoteCall: (id) dataThing
{
if([dataThing isKindOfClass:[NSMutableArray class]] == YES)
{
NSMutableArray *swArray = dataThing; // not strictly necessary, but good defensive practice
//do something with swArray
return YES;
}
return NO;
}
To be called like:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]; // you'll need to release this somewhere
Services * serv = [[Services alloc] init];
return [serv remoteCall:channelArray];
Since you don't return a different array in remoteCall:, channelArray's contents will be manipulated by the method and the YES/NO return value.
If there is some reason why the above seemingly won't work for you, please explain why.
Note: The code obviously requires an NSMutableArray if you are going to muck with the contents. The isKindOfClass: could be checking for NSMutableArray or NSArray and it wouldn't matter either way. When using arrays in your code and requiring a mutable array, it is up to you to make sure the data flow is correct such that you don't end up w/an immutable array where you need a mutable array.
If you don't need to reassign your variable, then don't use this. id or NSObject * is just fine and works by reference anyway. id * or NSObject ** would be references. id ** doesn't make sense at all here.
Also, learn naming conventions (like upper/lowercase).
NSArray is a class cluster. That means that every NSArray instance is actually an instance of some subclass. Only isKindOfClass: is useful for class-membership testing with class clusters.
Also... thats horrible code - please accept this:
-(BOOL)remoteCall:(id)dataClass {
if([dataClass isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *sw =(NSMutableArray *)dataClass;
return YES;
}
}
that should work.
Constructive critisism of coding: You need to adhere to coding conventions. Although your code will work... its not brilliant to read and theres a lot of unnecessary *s and such.
Function names should be camel coded with a preceeding lower-case letter as should variable names. Passing (id) into a function doesn't require *s at all. Objects you pass into a function only available throughout the scope of the method anyway and that method doesn't own it, I'm not sure what you're trying to do with all the extra *s, but just treat objects you pass into the method as if you don't own them. :)
As Eiko said before, i'd use just id and not double pointers to ID.
I'm also pretty sure that isMemberOfClass is your Problem. isMember does not check for inheritance, so you're only asking for Top level Classes. isKindOfClass is probably the better choice, as there is no guarantee that Apple doesn't use an internal subclass of NSMutableArray internally. Check the Apple Docs.
i'd write it as such:
-(BOOL)RemoteCall:(id)dataClass
{
if([dataClass isKindOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)dataClass;
//do something with SW
return TRUE;
}
}

Sorting an NSMutableArray containing matching "pairs" of values

I'd prefer not to change the way anArray is designed because it is also used elsewhere as a dataSource for a UITableView. The odd number values are "user names", and the even number values are their matching "dates". (The "pairs" must remain together.)
How would I sort "by user name"?
How would I sort "by date"?
Should I be using sortUsingFunction or sortUsingSelector?
-(void) test
{
NSMutableArray *anArray = [NSMutableArray arrayWithObjects:#"Zeke", #"01-Jan-2010", #"Bob", #"02-Jan-2010", #"Fred", #"03-Jan-2010", #"Susan", #"04-Jan-2010", #"Kim", #"05-Jan-2010", #"Debbie", #"06-Jan-2010", nil];
[anArray sortUsingFunction:SortByDate context:(void *)context];
}
NSComparisonResult SortByDate(NSMutableArray *a1, NSMutableArray *a2, void *context)
{
// What goes here to keep the pair-values "together"?
}
Why aren'e you using a NSDictionary? You can have user names as keys and the dates as corresponding values. Then if you want to sort on user names, just use [dictObj allkeys] to get an array containing just the keys. Sort them as you prefer and then when displaying display the sorted array of keys and fetch the corresponding value for the key and display along side. Similarly, you can get all the values using [dictObj allValues] sort them and display them along with keys.
I'd prefer not to change the way anArray is designed because it is also used elsewhere as a dataSource for a UITableView
Does that mean cells alternate between displaying usernames and dates, or are you multiplying/dividing by 2?
Either way, you need to fix your model. The bit where you go "oops, sorting doesn't work" should be a big hint that you haven't chosen a good representation of your data. Adding a workaround will only lead to more tears later.
I'd suggest something like this:
Create a class to represent your (username,date) tuple.
Add -compareByDate: and -compareByUsername: methods.
[array sortUsingSelector:#selector(compareByDate:)]
If you want to display usernames and dates in alternating cells, then it's still easy to do, but I'd simply use a taller cell and save myself some pain.
Note: The overhead of Objective-C method calls means that you can get a significant performance increase by using sortUsingComparator: and defining appropriate functions (you can stick the function between #implmentation and #end to let them access protected/private ivars) but it's not really worth doing this unless you've determined that sorting is a bottleneck.
Something like this could work:
#class SortWrapper: NSObject
#property (copy,readwrite,atomic) NSString* name;
#property (copy,readwrite,atomic) NSDate* date;
#end
...
- (void)test
{
NSArray* names = #[#"Alice", #"Bob", #"Cameron"];
NSArray* dates = #[aliceDOB, bobDOB, cameronDOB]; // NSDate objects
// turn into wrapped objects for sorting
NSMutableArray* wrappedObjects = [NSMutableArray array];
for ( int i=0; i<names.count; i++ ) {
SortWrapper* wrapped = [[SortWrapper alloc] init];
wrapped.name = names[i];
wrapped.date = dates[i];
[wrappedObjects addObject:wrapped];
}
// to sort by date:
NSArray* sortedByDate = [wrappedObjects sortedArrayUsingComparator:^NSComparisonResult(SortWrapper* obj1, SortWrapper* obj2) {
return [obj1.date compare:obj2.date];
}];
// to sort by name:
NSArray* sortedByName = [wrappedObjects sortedArrayUsingComparator:^NSComparisonResult(SortWrapper* obj1, SortWrapper* obj2) {
return [obj1.name compare:obj2.name];
}];
}

Declaring an object type in an if statement

I'm trying to declare a variable inside an if statement. If the result of a query is YES then the object will be of one type, otherwise it will be of another type. A bit like this...
if (YES) {
ObjectTypeA *object = [[ObjectTypeA] alloc] init];
}
else {
ObjectTypeB *object = [[ObjectTypeB] alloc] init];
}
Once that's done I want to use object with the same methods no matter what type it is. I tried declaring object as an id before the if statement but get an error: member reference type 'struct objc_object *' is a pointer; maybe you meant to use '->'?
I also tried declaring both to be separate objects outside the if and then make the pointer point to whichever it was once I knew. That wouldn't work either.
I know that the compiler is trying to protect me from myself by doing this but in this circumstance I need a way round it please.
Thanks.
The most common pattern for this problem in Cocoa/Cocoa Touch is to define a protocol.
A protocol is a collection of methods that can be implemented by any object.
If you make ClassA and ClassB conform to a protocol containing the methods you need them to respond to then you don't need to worry about which type of object you get.
The idea is "if it looks like a duck and quacks like a duck, then it's probably a duck".
You can use dynamic typing and create your objects depending on the outcome of your query, but ensure that the resulting object conforms to a particular protocol, like so:
id <MyProtocol> myObject;
if (YES)
myObject = [[ClassA alloc] init];
else
myObject = [[ClassB alloc] init];
[myObject myMethod];
[myObject release];
I think this code should work well:
id obj = nil;
if (YES) {
obj = [[ObjectTypeA] alloc] init];
} else {
obj = [[ObjectTypeB] alloc] init];
}
[obj performSelector:#selector(YOUR_METHOD) withObject:YOUR_OBJECT];
You want dynamic typing :)
The way around this is to declare a third object that both of these inherit from
OR
You could use the adapter pattern and create an object that accepts both of these objects as a member and then wrap the functions you wish to call into that object
good luck!
--> These are genral OO solutions; I'm not a Objective-C developer

How should I initialize an array that's a member of a UITableViewController?

The data source for my table view is a plain NSMutableArray that will be populated as the app runs, but will be empty when the Table View first loads. The class interface looks like this...
#interface ViewController_iPhone : UITableViewController {
NSMutableArray *serverList;
}
#property (retain, readonly) NSMutableArray *serverList;
#end
My questions are...
Currently, I initialize it in the viewDidLoad method like so...
serverList = [[NSMutableArray alloc] initWithCapacity:1];
I do it this way because the array needs to be valid in order for my numberOfRowsInSection method to avoid crashing when reading the count of the array (which will be zero) when the view first loads. My current approach of using initWithCapacity just feels a little clunky since I just need an empty, but valid array object that will return a count value of zero when the view loads. How should I be initializing my serverList array?
While playing around, I noticed that when I try and initialize the serverList array this way...
serverList = [NSMutableArray arrayWithCapacity:1];
it crashes on that line. Why?
Thanks in advance for all your help!
Here's a key concept to learn about member variables and properties: Member variables are not the same as properties.
That is, when accessing a member variable in your class:
serverList = [NSArray array]; is not the same as self.serverList = [NSArray array];
serverList by itself means you're accessing it directly.
self.serverList means you're using the getter/setter methods to access it.
Normally this isn't that big of a deal when dealing with basic variable types. However, when your property uses retain or copy, that means your setter method will automatically retain it when you use it, but it won't do such when you access it directly.
That means:
serverList = [NSArray array]; will not retain the array.
self.serverList = [NSArray array]; will retain the array.
It should be noted that [NSMutableArray arrayWithCapacity:1]; (and 99% of other methods that aren't alloc) will return an object that is autoreleased. If you want to keep it for later use, as you need to in this case, then you must retain it in some form or fashion.
I somehow missed the simplest approach and found that when I simply create the array like so...
serverList = [[NSMutableArray alloc] init];
and release it in the dealloc method, everything works great!
Note that capacity does not mean any objects are in the array. Using -initWithCapacity: simply sets aside a chunk of space for the array. Nothing is in the array even with a non-zero capacity.
The following initializers:
self.serverList = [[[NSMutableArray alloc] init] autorelease];
self.serverList = [[[NSMutableArray alloc] initWithCapacity:capacity] autorelease];
self.serverList = [NSMutableArray array];
self.serverList = [NSMutableArray arrayWithCapacity:capacity];
should all work, however.
Make sure you specify self so that the property is accessed. Using serverList by itself will not work.
Make sure you autorelease any alloc-init of a property which you are retaining, otherwise you will have a memory leak.
You do not need an array with a specified capacity. This is just a performance convenience for setting aside contiguous memory, and useful if you have a rough idea how much space your array will need up-front. An array that is created through alloc-init will also be empty, with a count of zero.