Objective-C: Getting the True Class of Classes in Class Clusters - iphone

Recently while trying to answer a questions here, I ran some test code to see how Xcode/gdb reported the class of instances in class clusters. (see below) In the past, I've expected to see something like:
PrivateClusterClass:PublicSuperClass:NSObject
Such as this (which still returns as expected):
NSPathStore2:NSString:NSObject
... for a string created with +[NSString pathWithComponents:].
However, with NSSet and subclass the following code:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSSet *s=[NSSet setWithObject:#"setWithObject"];
NSMutableSet *m=[NSMutableSet setWithCapacity:1];
[m addObject:#"Added String"];
NSMutableSet *n = [[NSMutableSet alloc] initWithCapacity:1];
[self showSuperClasses:s];
[self showSuperClasses:m];
[self showSuperClasses:n];
[self showSuperClasses:#"Steve"];
}
- (void) showSuperClasses:(id) anObject{
Class cl = [anObject class];
NSString *classDescription = [cl description];
while ([cl superclass])
{
cl = [cl superclass];
classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
}
NSLog(#"%# classes=%#",[anObject class], classDescription);
}
... outputs:
// NSSet *s
NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
//NSMutableSet *m
NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
//NSMutableSet *n
NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
// NSString #"Steve"
NSCFString classes=NSCFString:NSMutableString:NSString:NSObject
The debugger shows the same class for all Set instances. I know that in the past the Set class cluster did not return like this.
What has changed? (I suspect it is a change in the bridge from Core Foundation.)
What class cluster report just a generic class e.g. NSCFSet and which report an actual subclass e.g. NSPathStore2?
Most importantly, when debugging how do you determine the actual class of a NSSet cluster instance?

An implementation detail changed most likely related to the bridging to CF. If you look closely, you'll see that a bunch of the Objective-C implementation backing various Foundation classes can now be found in CoreFoundation.
Another implementation detail. The implementation of the Foundation class clusters change -- sometimes greatly, sometimes subtly -- with each release of Mac OS X. The exact set of private classes related to said implementation is generally fallout from whatever particular set of requirements were needed to achieve a flexible and reasonably optimal solution while also preserving the API contract of the public facing classes.
NSCFSet is the actual class. The features of the instances are determined by how you allocated it in the first place. There is no exposed way to determine if your set is mutable or immutable. This is actually on purpose; going down the path of writing a lot of code that changes behavior based on the mutability of a class cluster leads to madness.

Related

Objective-C method to nullify object

i have some trouble writing a method in Objective-C to make an object nil. Here is some example :
#interface testA : NSObject
{
NSString *a;
}
#property (nonatomic, retain) NSString *a;
+(testA*)initWithA:(NSString *)aString;
-(void)displayA;
-(void)nillify;
#end
#implementation testA
#synthesize a;
+(testA*)initWithA:(NSString *)aString{
testA *tst=[[testA alloc] init];
tst.a=aString;
return [tst autorelease];
}
-(void)displayA{
NSLog(#"%#",self.a);
}
-(void)nillify{
self=nil;
}
- (void)dealloc {
[a release];
[super dealloc];
}
#end
int main(int argc, char **argv){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
testA *test=[testA initWithA:#"some test"];
[test displayA];
test=nil;
//[test nillify];
NSLog(#"after setting to nil");
[test displayA];
[pool release];
return 0;
}
Apparently , when I set test object to nil and then call some method on it nothing happens , but if i call nillify instead of directly setting it to nil , displayA method works normally like test object is still there. Is there a workaround for nillify method to function properly ?
Your help is much appreciated !
You can't actually do something like this, because setting 'self' to nil only has any effect within the scope of that method (in your case, 'nilify'). You don't have any actual way to effect the values of pointers located on other parts of the stack or in random places in the heap, for example.
Basically any code that holds a reference to some object is responsible for maintaining and clearing those references itself. If you have some use case where random sections of code may need references to "live" objects of some kind, but where you'd want those object references to go away in response to some external event (maybe a user tracking system or something), you could do something with notifications, but the various modules tracking those "live" objects would still be responsible for listening for notifications and cleaning up references when they received them.
The 'nilify' thing, however, can't possibly work.
You cannot do what you're trying to do. self is just a local reference to an object that actually exists elsewhere. Setting it to nil doesn't mean anything. An object doesn't, in general, own itself, and it certainly doesn't control other objects' references to it. It's up to the owning objects to manage its lifetime.
There are a few things wrong with your code.
First, by convention, class names start with an uppercase letter. Please stick to these naming conventions as it will make it harder for other developers to work with your code (and even confuse you).
Next, your initWithName:... According to the naming conventions, a method with init in its name should be an instance method, not a class method. So either name it newWithName: or turn it into an instance method like this:
-(testA*)initWithA:(NSString *)aString{
self = [super init];
if (!self) return nil;
tst.a=aString;
return self;
}
If you keep it as class method (and name it newWithName:) you should not return a autoreleased object since according to the naming conventions method that start with init... or new... return a retained object. If you do not follow these conventions, the static analyzer will give you "false" warnings and it will become useless for you.
Now for the reason your nillify doesn't work: the self is in fact an argument to a method. Under the hood, your nillify method actually has two arguments that you do not see: the self pointer and the selector pointer. This means, self is actually a variable on the stack. And if you overwrite it, you only overwrite that stack variable but that doesn't influence your test variable which is somewhere else.
As an example, consider a method - (void)foo:(NSString *)bar;. The compiler turns it into the equivalent of the C function (void) foo(id self, SEL _cmd, NSString *bar).

question related to Iphone autorelease usage

Could someone help me please understand how allocation and memory management is done and handled in following scenario. i am giving a Psuedo code example and question thats troubling me is inline below:
interface first
{ NSDecimalNumber *number1;
}
implementation
.....
-(void) dealloc {
[number1 release];
[super dealloc];
}
=================================
interface second
{ NSDecimalNumber *number2;
}
implementation second
.....
- (First*) check
{
First *firstObject = [[[First alloc] init] autorelease];
firstObject.number1 = [[NSDecimalNumber alloc] initWithInteger:0];
**// do i need to autorelease number1 as well?**
return firstObject;
}
Your code is correct as is. If you autoreleased the object, its reference count would reach zero and it would be dealloced, and then if you later tried to use the value stored in number1 your app would crash.
The only enhancement I'd add is releasing any existing value. i.e.
[number1 release];
number1 = [[NSDecimalNumber alloc] initWithInteger:0];
If you don't do this, the previous object assigned to number1 will leak each time check is called.
As you're allocing the NSDecimalNumber, you need to release it. (As you're doing so in the dealloc.)
Whilst it's hard to provide a meaningful example based on your sample code (as "number1" isn't actually used), the general rule is that you're responsible for any object you alloc, copy or new. If the object was only required in the scope of a function, you could of course autorelease it.
There's a good blog article over at http://interfacelab.com/objective-c-memory-management-for-lazy-people/ that I'd recommend reading as it provides good examples (including some edge cases) and is easy to follow.

iphone NSString Array

I declared
NSString *dayinfield[43];
and fill it in
-(void)DrawDemo {
dayinfield[2] = #"hallo";
dayinfield[3] = #"test";
// also i can read it
NSLog (#"show: %#",dayinfield[2]);
//works fine
}
but when i like to read its content in another function (same class)
-(void)ReadData
{
NSLog (#"show: %#",dayinfield[2]);
// I get random infos or “EXC_BAD_ACCESS
}
How do I initialize the NSString Array correct so I can reach its content in each of my functions??
Thanks
chris
If you only assign literals to the array elements, this should not be a problem. But if you use other strings, you have to retain the instances manually when using a C array.
By the way: Objective-C methods start with a lowercase letter.
This would happen if you never initialized the array (or the parts of it you are accessing) - if you haven't called -DrawDemo before -ReadData or used different indices than the ones posted here, the array would simply contain garbage values.
Try to initialize the array contents to nil or #"" in your initializer method and see if the problem persists.
Alternatively consider using a suitable Cocoa container.
It's memory is probably being released before your second call. Assuming you have declared dayinfield as an ivar (and the fact that you don't get bad access all the time) your string aren't properly retained.
Initialise the strings like this:
dayinfield[2] = [[NSString alloc] initWithString:#"hallo"];
dayinfield[3] = [[NSString alloc] initWithString:#"test"];
and you should release them after you're class is being deallocated (See Memory Management Guide).
Also, obviously it depends on what you want to do, but it might be easier if you use NSArray instead of C arrays.
What you have in the OP should work although it is an exercise in sheer masochism to use old school C arrays with objects.
I ran this code:
#interface TestClass : NSObject {
NSString *a[1];
}
- (void) drawDemo;
- (void) readData;
#end
#implementation TestClass
- (void) drawDemo{
a[0]=#"A Zero";
a[1]=#"A One";
}//------------------------------------- (void) drawDemo------------------------------------
- (void) readData{
NSLog(#"a[0]=%#,a[1]=%#",a[0],a[1]);
}//------------------------------------- (void) readData------------------------------------
#end
TestClass *tc=[[TestClass alloc] init];
[tc drawDemo];
[tc readData];
... and got this output:
a[0]=A Zero,a[1]=A One
Your problem is elsewhere in your code. There is no compelling reason to use C arrays with objects. You gain nothing and you have to watch them like a hawk to prevent errors.

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.

Initialize a class only once

I have a class that contains a few instance methods which need to be called from another class. I know how to do that -
TimeFormatter *myTimeFormatter = [[TimeFormatter alloc] init];
[myTimeFormatter formatTime:time];
However, I don't want to have to alloc and init TimeFormatter every time I need to call one of its methods. (I need to call TimeFormatter's methods from various methods in another class).
I tried putting
TimeFormatter *myTimeFormatter = [[TimeFormatter alloc] init];
"by itself", or not in any blocks, but when I compile, I get an "initializer element is not constant" error.
Any input is greatly appreciated!
You can use the singleton pattern. You can read more about it here.
Specifically, you'd do something like:
static TimeFormatter* gSharedTimeFormatter = nil;
#implementation TimeFormatter
+ (TimeFormatter*)sharedTimeFormatter {
if (!gSharedTimeFormatter) {
#synchronized(self) {
if (!gSharedTimeFormatter) {
gSharedTimeFormatter = [[TimeFormatter alloc] init];
}
}
}
return gSharedTimeFormatter;
}
...
#end
Notice that we check if the variable is null, and if it is, we take a lock, and check again. This way, we incur the locking cost only on the allocation path, which happens only once in the program. This pattern is known as double-checked locking.
However, I don't want to have to alloc and init TimeFormatter every time I need to call one of its methods. (I need to call TimeFormatter's methods from various methods in another class).
I think it's worth clarifying some OOP terminology here.
The reason you need to alloc and init TimeFormatter is because your methods are instance methods. Because they're instance methods, you need an instance, and that's what alloc and init provide. Then you call your methods on (send messages to) the instance ([myTimeFormatter formatTimeString:…]).
The advantage of allowing instances is that you can keep state and settings in each instance, in instance variables, and make the latter into publicly-visible properties. Then you can deliberately have multiple instances, each having its own settings configured by whatever's using that instance.
If you don't need that functionality, you don't need to make these instance methods. You can make them class methods or even C functions, and then you don't need a TimeFormatter instance. With class methods, you send messages directly to the class ([TimeFormatter formatTimeString:…]).
And if you do want settings shared among all instances (and you don't have any state to keep), then you're right that you can just have one instance—a singleton.
The reason for that parenthesis is that shared state is bad, especially if two threads may use the time formatter concurrently. (For that matter, you could say that about settings, too. What if one thread wants seconds and the other doesn't? What if one wants 24-hour and the other wants 12-hour?) Better to have each thread use its own time formatter, so that they don't get tripped up by each other's state.
(BTW, if TimeFormatter is the actual name of your class: You are aware of NSDateFormatter, right? It does let you only format/parse the time.)
Here's a detail example of a sharedMethod. Credit goes here
#implementation SearchData
#synthesize searchDict;
#synthesize searchArray;
- (id)init {
if (self = [super init]) {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"searches.plist"];
searchDict = [[NSDictionary alloc] initWithContentsOfFile:finalPath];
searchArray = [[searchDict allKeys] retain];
}
return self;
}
- (void)dealloc {
[searchDict release];
[searchArray release];
[super dealloc];
}
static SearchData *sharedSingleton = NULL;
+ (SearchData *)sharedSearchData {
#synchronized(self) {
if (sharedSingleton == NULL)
sharedSingleton = [[self alloc] init];
}
return(sharedSingleton);
}
#end
A very nice, and easy, way to setup a Singleton is to use Matt Gallager's SYNTHESIZE_SINGLETON_FOR_CLASS.
It sounds like you want to make TimeFormatter a singleton, where only one instance can be created. Objective-C doesn't make this super easy, but basically you can expose a static method that returns a pointer to TimeFormatter. This pointer will be allocated and initialized the first time in, and every time after that same pointer can be used. This question has some examples of creating a singleton in Objective-C.
You are trying to declare your variable outside the class? If to do it the way you want to do it you gotta declare it as static so
static TimeFormatter *myFormatter=...
From the name of the class though i dont see why you would wnat to keep one instance of your class... you can also do this with a singleton as described above, that is if you want to keep one instance of your class for the app as a whole.