Using the value of a string to determine the instance of a class - iphone

I have a switch statement similar to this one:
switch (number)
{
case 1:
if (imageView1.hidden == NO)
{
imageView1.hidden = YES;
} else
{
imageView1.hidden = NO;
}
break;
case 2:
if (imageView2.hidden == NO)
{
imageView2.hidden = YES;
} else
{
imageView2.hidden = NO;
}
break;
And so forth and so on.
My question is how do I use a string with a value say "imageView1" and use that to access the instance of my imageView class instead of having a different case for each instance of imageView? I know it muse be similar to creating an NSPath from a string or something like that, but I'm just not sure where to look or what it would be called.
Thanks in advance for any help!

I don't disagree with those who are concerned about the design, if this is actually the code. I will assume, however, that you are only posting a generalized version of your question. And since this is an important concept in Objective-C, so we should talk about it.
You can access an object's properties by name using Key Value coding, and the routine -valueWithKey:.
NSString *nameOfView = #"imageView1";
[[self valueForKey:nameOfView] setHidden:YES];
This will, in order, look for a method called -imageView1, an ivar named imageView1 and finally an ivar named _imageView1. This technique is very heavily used in Cocoa, and is important to understand. This is one of the many reasons we name things carefully, and yet another reason that we make accessors that handle memory management for us. Search the docs for "Key-Value Compliance" for more information.
Now for this specific case, I would tend towards something more like JimG's solution, using an NSArray of views, so I can loop through them and turn on or off the ones I want based on their index. But I can imagine a lot of cases where that wouldn't be appropriate, and KVC may be.

Why not put the instances in an NSArray and index into that?
NSArray *views = [NSArray arrayWithObjects: imageView1, imageView2, nil];
NSImageView *iview = [views objectAtIndex: number];
Also, you could consider something like:
iview.hidden = ! iview.hidden;
[Edit: missing asterisks, oops]

Related

custom setter for NSString

I have a NSString called fontType
and I am trying to have a custom setter for it:
- (void) setFontType:(NSString *) fType
{
if (self.fontType != fType){
[fontType release];
self.fontType = [fType retain];
//some more custom code
}
}
Is there any issue with this?
A few things that stand out for me:
do not use self. inside of custom accessors. access the variable directly
it's better use copy semantics for properties of a type that has a
mutable subtype
be careful with whatever is // some more custom code
My personal style preferences are like so:
-(void)setFontType:(NSString *)fontType_ {
if (fontType == fontType_) return; // get out quick, flatten the code
[fontType release];
fontType = [fontType_ copy];
// some more code
}
Cocoa with Love has a good article on this topic. It's worth a read.
When you do self.fontType = newFontType, you are doing [self setFontType:newFontType] (just with a different syntax), this means you are calling the method inside itself.
This is called recursion, but in this case, you don't have a base case in which the method will stop calling itself, so my guess is that this method will call itself until the app crashes. In short, recursion is not something you want here.
Just replace self.fontType = [fType retain] with fontType = [fType retain] (Assuming the var linked to the fontType property is called fontType as well).
PS. At the end of the question you asked
Is there any issue with this?
If you didn't try this, then you shouldn't even be asking that here on StackOverflow, and if you did try it, then you should have realized that this method didn't work, so that last line is pretty meaningless. Just saying.

How to get NSString variable value from NSObject to ViewController

I am trying to set up an object to control all of my data so it can set things up in the background to it appears my tableviews load faster than they do now etc.
This is what I am trying to achieve.
I am setting a variable in the NSObject from the secondVC when the tableviewcell is selected like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Access selected cells content (cell.textLabel.text)
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//Parent view logic (sends info back to the correct cell in parent view)
if (parentViewSelectedIndexPath.section == 0)
{
if (parentViewSelectedIndexPath.row == 0)
{
//Predicates restrict the values that will be returned from the query
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"MANUFACTURER",cell.textLabel.text];
NSArray *filterArray = [myDataArray filteredArrayUsingPredicate:predicate];
//[[self delegate] setManufactureSearchFields:filterArray withIndexPath:indexPath]; //This is where I pass the value back to the mainview
//Using Object
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
[vehicleControllerNSObject setFirstCell:filterArray];
}
//etc
At the end there you can see the method that is getting set up in the VechicleControllerNSObject which looks like this.
-(void)setFirstCell:(NSArray *)array{
manufactureSearchObjectStringFVC = [[array valueForKey:#"MANUFACTURER"] objectAtIndex:0];
NSLog(#"%#", manufactureSearchObjectStringFVC); // this prints the correct value to the console
}
As you can see this prints the correct output fine.
however I have no idea how to call manufactureSearchObjectStringFVC and pass the value it holds into the uitableviewcell that I would like to pass it in on my firstviewcontroller.
This is what I have for testing atm.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
manufactureSearchObjectString = vehicleControllerNSObject.manufactureSearchObjectStringFVC;
NSLog(#"%#", vehicleControllerNSObject.manufactureSearchObjectStringFVC);
}
That nslog prints null..
I have three questions
1, how do I get the correct value into the first valuecontroller.
2, should I be using viewDidAppear like this?.. I think not.. how can I do this better
3, Do you think this is a good way of doing this type of thing, as in the future i would like to use the NSObjectClass to parse info, cache etc all behind the senses leaving the views to just display when the data is ready hopefully helping performance..
Any help would be hugely appreciated as I really want to learn this stuff as i know its important for me to know.
Your question is so beautifully and clearly formatted and diagrammed that it seems a shame to ask you to do a search. But here it is:
Search for Sharing Data between View Controllers
You'll find many good discussions about sharing data between view controllers.
Briefly, though, I can tell you why your code isn't working. In your tableView:didSelectRowAtIndexPath: method, you are creating (alloc/init) a new instance of your VehicleControllerNSObject class each time. Then back in your first view controller on viewDidAppear:, again you are creating (alloc/init) a whole new instance each time.
So you have multiple objects coming and going and they have nothing to do with each other. It's a bit like giving some important information to one person at a bus station and then later randomly picking some other person out and trying to retrieve that same information from her.
So one quick idea would be to create just once instance of your VehicleControllerNSObject (just an aside, that's a bit of a strange name for a class since generally all objective-c objects are descendants of NSObject anyway. I'm just going to call that VehicleController for now)
So let's say you wanted a 'sharedInstance' of VehicleController. You could add a class method to VehicleController to give you a way to easily get that one sharedInstance:
+(VehicleController*)sharedInstance {
static VehicleController *sharedInstance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_ = [[VehicleController alloc] init];
});
return sharedInstance_;
}
So to get that instance in methods in other classes you can just do something like :
VehicleController *sharedController = [VehicleController sharedInstance];
sharedController.someProperty = someValue;
// and then back in your first view controller, similarly:
VehicleController *sharedController = [VehicleController sharedInstance];
id someValue = sharedController.someProperty;
Again, check the search, many people have had good discussions on this. This is just one approach. I hope it at least makes sense why your code wasn't working.
Hope that helps.
To answer question 3. No.
I think that the best way to do something like this would be to use Core Data and it's NSManagedObject.
A combination of UITableViewController and NSFetchedResultsController that is feed from a Core Data sqlite backing store, if well set would feed and keep your UITableView updated.
It would be to long to describe all in here. So I will stop there.
If you don't want to go with that there is always the possibility to use a shared pointers to a mutable object or to use a singleton object to communicate information between UIViewController.

Check if something exists in an NSMutableArray

I have an NSMutableArray which can hold several objects. I would like to check if an object exists, and if so, alter it. I was wondering about checking it. I thought this would work:
if ([[[self.myLibrary objectAtIndex:1] subObject] objectAtIndex:1]) // do something
However, this will crash if there aren't any subObjects at index 1.
So I guess the problem is that the above does not return nil if there isn't anything at this Index.
Is there another easy way to check or will I have to count through the array etc.? I know there are other posts on stackoverflow on this, but I haven't found a simple answer yet.
Any explanations / suggestions welcome. Thanks!
No check simply using :
[myArray indexOfObject:myObject];
or
[myArray containsObject:myObject];
These methods check every object using isEqual.
For example:
NSUInteger objIdx = [myArray indexOfObject: myObject];
if(objIdx != NSNotFound) {
id myObj = [myArray objectAtIndex: objIdx];
// Do some alter stuff here
}
If this is a pattern you use a lot, you could add a category to NSArray called something like safeObjectAtIndex:, which takes an index, does the bounds checking internally:
-(id)safeObjectAtIndex:(NSUInteger)index {
if (index >= [self count])
return nil;
return [self objectAtIndex:index];
}
Assuming the object you are using to search with and the actual object in the array are the exact same instance, and not two different objects that are equal according to an overridden isEqual: method, you can do this:
if ([array containsObject:objectToSearchFor]) {
// modify objectToSearchFor
}
If the two objects are different instances which are equal according to isEqual:, you will have to use code like this:
NSUInteger index = [array indexOfObject:objectToSearchFor];
if (index != NSNotFound) {
id objectInArray = [array objectAtIndex:index];
// modify objectInArray
}
NSArray (which is the NSMUtableArray superclass) has lots of methods for finding objects. Have a look at the documentation.
You can either rely on the equals method (e.g. indexOfObject:) or provide a block (e.g indexOfObjectPassingTest:) which is pretty funky.
It's fairly common in Objective C to be using the Mutable version of a class but rely on methods in the non mutable superclass so it's always a good idea when checking the online documentation to look at the superclass.

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;
}
}

Does NSClassFromString affect performance?

I want to create a controller that depends on the class of a given instance of a model
-(BaseController *)getControllerForModel:(Model *)model
{
BaseController *controller = nil;
Class controllerClass = [BaseController class]; //the default value
//find the right controller
if ([model isMemberOfClass:[ModelClass1 class]])
controllerClass = [ConcreteController1 class];
else if ([model isMemberOfClass:[ModelClass2 class]])
controllerClass = [ConcreteController2 class];
else if ([model isMemberOfClass:[ModelClass3 class]])
controllerClass = [ConcreteController3 class];
...
else if ([model isMemberOfClass:[ModelClassX class]])
controllerClass = [ConcreteControllerX class];
else
Trace(TRACELEVEL_WARNING, #"Unrecognized model type: %#", NSStringFromClass([model class]));
//Now instantiate it with the model
controller = [[[controllerClass alloc] initWithModel:model] autorelease];
return slotController;
}
I want to find a more flexible solution to this and thought of having a dictionary, which maps Model-Classes to Controller-Classes and then NSClassFromString could give me the right instance.
My question is this: Is NSClassFromString using much of my applications performance if i use it several times (say, 100 times at once)? Or would it be about as fast as the above approach?
Generally, use of isMemberOfClass: in such a fashion indicates an architectural issue.
In this case, why can't the various Model classes simply implement a +controllerClass method?
I can understand Nikolai's desire to maintain the layering of Controller on top of Model without the Model having knowledge of the Controller. However, my experience is that any control layer that has such type specific knowledge of the Model quickly destroys that isolation anyway; the model layer quickly evolves behaviors that are controller specific.
Thus, the added complexity injected by attempting to maintain that separation just isn't worth it.
A Class is an id, and as such can be added to an NSDictionary. You should try the following:
mModelToControllerMap = [[NSDictionary alloc] initWithObjectsAndKeys:
[ConcreteController1 class] , [ModelClass1 class] ,
[ConcreteController2 class] , [ModelClass2 class] ,
...
nil];
Then later:
controllerClass = [mModelToControllerMap objectForKey:[modelInstance class]];
if ( controllerClass ) ...
else ...
If you make it a mutable dictionary, you can then have controllers register for the models they want instead of forcing the base class to be aware of all derived classes.
And a direct answer. At worst, NSClassFromString may need to iterate through all classes to find a match. It could also be a dictionary lookup, but the implementation is not documented and should not be relied on. While doing it a few hundred times probably isn't too bad, there are better ways.
If you're really concerned about performance, you could cache the results of the NSClassFromString and put them in a dictionary.
On the other hand, that's probably pretty much what NSClassFromString really does (a map lookup). So I'd say it's much faster that the 100x if approach.
But anyway: Just give it a try, as with all performance issues.
NSStringFromClass shouldn't be much more expensive. In fact, in your case you could use it much more effectively. I don't know your exact class naming convention, but using a methof like this could be much smaller and faster:
NSString * modelClassName = NSStringFromClass([model class]);
// get the suffix of class name
// for example, '3' in 'ModelClass3'
NSString * suffix = [modelClassName substringFromIndex:10];
// create the controller name
NSString * controllerName = [NSString stringWithFormat:#"ConcreteController%#", suffix];
/* do something if name is not valid */
controllerClass = NSClassFromString(controllerName);
as bbum suggested you could also make this a method on a common parent of the model classes.