Why does NSString respond to appendString? - objective-c

I was playing with the respondsToSelector method in Objective-C on MacOS-X 10.6.7 and Xcode 4.0.2, to identify if an object would respond to certain messages. According to the manuals, NSString should not respond to appendString: while NSMutableString should. Here's the piece of code which tests it:
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *myString = [[NSString alloc] init];
if ([myString respondsToSelector:#selector(appendString:)]) {
NSLog(#"myString responds to appendString:");
} else {
NSLog(#"myString doesn't respond to appendString:");
}
// do stuff with myString
[myString release];
[pool drain];
return 0;
}
and here's the output:
Class02[10241:903] myString responds to appendString:
I'd sort of expected the opposite. How does an NSString object respond to appendString: ? What's going on here that I'm missing ?

Short answer: That string is of type NSCFString, a class that inherits from NSMutableString, hence it responds to the selectors for the methods declared in NSMutableString, including superclasses.
Not so short answer: Foundation strings are toll-free bridged with Core Foundation strings. Developers use the opaque types CFStringRef (bridged with NSString) and CFMutableStringRef (bridged with NSMutableString) to refer to these strings so, at first glance, there are two different types of strings: immutable and mutable.
From a Core Foundation internal implementation perspective, there’s a private type called struct __CFString. This private type keeps a bit field that stores, amongst other information, whether the string is mutable or immutable. Having a single type simplifies implementation since many functions are shared by both immutable and mutable strings.
Whenever a Core Foundation function that operates on mutable strings is called, it first reads that bit field and checks whether the string is mutable or immutable. If the argument is supposed to be a mutable string but it in fact isn’t, the function returns an error (e.g. _CFStringErrNotMutable) or fails an assertion (e.g. __CFAssertIsStringAndMutable(cf)).
At any rate, these are implementation details, and they might change in the future. The fact that NSString doesn’t declare -appendString: doesn’t mean that every NSString instance doesn’t respond to the corresponding selector — think substitutability. The same situation applies to other mutable/immutable classes such as NSArray and NSMutableArray. From the developer perspective, the important thing is that the object that’s been returned is of a type that matches the return type — it could be the type itself or any subtype of that type. Class clusters make this a tad more convoluted but the situation is not restricted to class clusters per se.
In summary, you can only expect that a method returns an object whose type belongs to the hierarchy (i.e., either the type itself or a subtype) of the type for the return value. Unfortunately, this means that you cannot check whether a Foundation object is mutable or not. But then again, do you really need this check?
You can use the CFShowStr() function to get information from a string. In the example in your question, add
CFShowStr((CFStringRef)myString);
You should get an output similar to:
Length 0
IsEightBit 1
HasLengthByte 0
HasNullByte 1
InlineContents 0
Allocator SystemDefault
Mutable 0
Contents 0x0
where
Mutable 0
means that the string is in fact immutable.

This probably has to do with the implementation. NSString is a class cluster, which means that NSString is just a public interface and the actual implementing class is different (see what the class message gives you).
And at the same time NSString is also toll-free bridged to CFString, meaning that you can switch before those two types freely just by casting:
NSString *one = #"foo";
CFStringRef two = (CFStringRef)one; // valid cast
When you create a new string you really get a NSCFString back, a thin wrapper around CFString. And the point is that when you create a new mutable string, you also get an instance of NSCFString.
Class one = [[NSString string] class]; // NSCFString
Class two = [[NSMutableString string] class]; // NSCFString
I guess this was convenient from the implementation point of view – both NSString and NSMutableString can be backed by a common class (= less code duplication) and this class makes sure you don’t violate the immutability:
// “Attempt to mutate immutable object with appendString:”
[[NSString string] appendString:#"foo"];
There’s a lot of guess work in this answer and I don’t really understand the stuff, let’s hope somebody knows better.

You should not make assumptions about a method being not there. That method might be used internally or for whatever reason it exists. Technically, it's just private API.
You only have a contract to the public declarations (docs), and they don't show that message. So be prepared to get into trouble rather quickly if you use other features.

Related

NSFastEnumeration object casting in ARC

I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.

Getting the class type for a nil object?

If I have an object that is already allocated, then doing object.class returns a non-nil value. So far so good. But, if the object has not yet been allocated, then accessing object.class returns nil.
I want to allocate an object based on its type dynamically, so for example:
#property NSArray *myArray;
...
// myArray is nil so far
self.myArray = [_myArray.class new];
However, I can't do this because _myArray.class is returning nil. So how would I determine the class type of a nil instance?
Update:
It is in fact possible. Check out my answer below.
You cannot determine the class of a nil instance, because it does not have one: it can be, quite literally, of any type derived from the type of the variable. For example, NSMutableArray is perfectly compatible with NSArray:
NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK
Since the run-time capabilities of different subclasses can vary a lot, it is always up to your program to decide what kind of objects it wants.
Objective-C is a duck-typed language. This means that there are several things you can or can't do, and one of the things you can't is statically get a reference to the type of a variable.
Specifically, in your expression:
[_myArray.class new]
First, _myArray.class is evaluated, and then the result is sent the new message. Since _myArray is nil to begin with, _myArray.class returns nil as well, and the new message will return nil too, because sending any message to nil returns nil (or the closest representation to zero the return type has). This is why it doesn't work.
I suspect you come from a strongly-typed language like C#; what you're doing right now is the equivalent of Foo foo = (Foo)Activator.CreateInstance(foo.GetType()), which is sure to fail because foo.GetType() will either not compile or throw an exception (depending on if it's a class field or a local variable) since it was never assigned a value. In Objective-C, it compiles but it doesn't works. What you would want is Activator.CreateInstance(typeof(Foo)), but notice that Foo is now hardcoded here too, so you might as well just create a new Foo().
You say that the compiler "knows the type" of the object. This is not exactly true. First, NSArray and NSMutableArray are the root classes of the NSArray class cluster. This means that both are abstract, and [NSArray alloc] and [NSMutableArray alloc] return an instance of a subclass (NSCFArray last time I checked, and possibly something else; I recall seeing _NSArrayM). Maybe [NSArray new] works, but it's not giving you a plain NSArray.
Second, type safety is not enforced. Consider this code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
So even though the compiler thinks that bar is an NSArray, it's in fact a NSString. If we plug in your code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];
baz is now an NSString as well. Since you ask for the runtime class of bar, the compiler has nothing to do with the operations.
And precisely because of that kind of behavior, you should probably instantiate your object with a class that you know, using [NSArray new] instead of trusting _myArray to be non-nil, and to be what you think it is.
You must init the property , or it will be nil , send a message to a nil object , it will return nil , so ,you must first init the array like _array = [[NSArray alloc] init];
So, for anyone wondering if this is possible, it is:
objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];
strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);
NSLog(#"%#", objectClass);
Output:
NSArray
Done with the help of extobjc.
Nil has no class type
In Objective-C the actual class on an instance variable is only determined at runtime. So, you can't know the class of a nil object.
This is not an issue in your situation since you only need to do:
NSArray *myArray = [NSArray new];
Or
NSArray *myArray = [[NSArray alloc] init];
In Objective-C most decisions are deferred to the runtime
(as much as possible)
Objective-C is a runtime oriented language, which means that when it's
possible it defers decisions about what will actually be executed from
compile & link time to when it's actually executing on the runtime.
This gives you a lot of flexibility in that you can redirect messages
to appropriate objects as you need to or you can even intentionally
swap method implementations, etc.
This requires the use of a runtime
which can introspect objects to see what they do & don't respond to
and dispatch methods appropriately. If we contrast this to a language
like C. In C you start out with a main() method and then from there
it's pretty much a top down design of following your logic and
executing functions as you've written your code. A C struct can't
forward requests to perform a function onto other targets.
Source: Understanding the Objective-C Runtime

Objective-C: a Pointer That Points to Difference Classes at Different Time

I'm not really experienced with Objective-C. Here is a problem I encountered.
When I want to define a pointer for a particular instance of a class, I can
NSString* foo;
But is it possible to define pointers for instances of classes like this?
x* hotdog; //"x" is the type of pointer hotdog is
hotdog = NSString; //now points to NSString
hotdog* foo; //an instance of NSString is created
hotdog = UIView; //now points to UIView
hotdog* foo; //an instance of UIView is created
How to define the class-pointer hotdog? (what should I replace x with?)
what should I replace x with?
You should replace x with the name of the most specific common ancestor of the classes that you are planning to use with this pointer. In your example, that would be NSObject, because both NSString and UIView inherit it, and there are no other common ancestors. In the worst case, the common ancestor is id.
In general, tricks like that should be avoided in most situations, because reusing a pointer for something really different is bad for readability.
If you want a pointer to an object of a type that's not yet known at compile-time (similar to dynamic in C#), use id:
id hotdog;
hotdog = [[NSString alloc] init];
hotdog = [[NSArray alloc] init];
Only do this when you really need it. If you use it everywhere, your code can easily become a mess since you'll lose track of the type of the variable.
At first I misunderstood your question. I'll leave my old answer here just in case future visitors need it.
The type of pointers to classes is Class and to get an object of that type use +[NSObject class].
Class hotdog = [NSString class]; // now points to NSString
NSString *myString = [[hotdog alloc] init]; // create instance of NSString
hotdog = [NSArray class]; // now points to NSArray
NSArray *myArray = [[hotdog alloc] init]; // create instance of NSArray
You can use either NSObject* or id as the pointer type. NSObject* will accept any subclass of NSObject, while id will accept other Objective-C objects as well.
Note that, to avoid compiler warning messages, you must cast the pointer type back to the (presumably known) actual type before applying any sort of dereferencing operation (other than methods of NSObject).
You can, to be sure you have the expected type of object, use isKindOfClass to check the type:
if ([genericPointer isKindOfClass:[NSArray class]]) {
NSString* arrayElement = [(NSArray)genericPointer objectAtIndex:x];
}
But is it possible to define pointers for instances of classes like this?
I suppose you're asking for the equivalent of C++ templates.
You can't do it and you don't need it, just use the id type:
id foo= #"some text";
If you are working on an instance class the pointer to the class itself is simply self.
If you are working on a class pointer you could just use the id type since it is a generic type. Make sure then the object you are working on is of the expected type by using the isKindOfClass method if you want to invoke some methods of this class.

Why are these two NSString pointers the same?

When I alloc and init two NSString variables and compare their pointers, they are the same. Here's a snippet that shows this:
NSString *s1 = [[NSString alloc] initWithString:#"hello world"];
NSString *s2 = [[NSString alloc] initWithString:#"hello world"];
if (s1 == s2) {
NSLog(#"==");
}else {
NSLog(#"!=");
}
Why are s1 and s2 the same?
There are three things going on here:
Firstly, the two identical string literals you're passing in to initWithString: will have the same address to start. This is an obvious optimization for constant data.
Secondly, when you nest alloc and init with strings, the runtime performs an optimization, the alloc call essentially becomes a no-op. This is done using the NSPlaceholderString class. This means the pointer you get back here will be coming from initWithString:, not from alloc.
Thirdly, under the hood, initWithString: is calling CFStringCreateCopy, which as you may find, has the following behavior: Since this routine is for creating immutable strings, it has an optimization. It simply calls CFRetain() and returns the same object that was passed in.
Thanks for the very interesting question. I had fun figuring it out.
#"hello world" strings are of class NSConstantString.if you use #"hello world" in two places, they will be referencing the very same object.
From documentation.
The simplest way to create a string object in source code is to use
the Objective-C #"..." construct:
NSString *temp = #"/tmp/scratch"; Note that, when creating a string
constant in this fashion, you should use UTF-8 characters. Such an
object is created at compile time and exists throughout your program’s
execution. The compiler makes such object constants unique on a
per-module basis, and they’re never deallocated, though you can retain
and release them as you do any other object. You can also send
messages directly to a string constant as you do any other string:
BOOL same = [#"comparison" isEqualToString:myString];

confused about NSString *str

I am kind of confused by the behavior of NSString *str..
I assigned it in several ways, sometimes it works, and sometimes it becomes null.
NSString *str = #"/hi/hello"; // this one always works
// this sometimes becomes null after the function ends
NSString *str2 = [str lastPathComponent];
// as above
NSString *str3 = [NSString stringWithString:str2];
NSString *str4 = [NSString initWithString:str3];
I am not quite familiar with the object behavior of Obj-C, is it just like C++?
If so, how can I do assignment safely like
string str = "hi";
string str2 = str;
behaves in C++?
ex: I declare a string in my .h file,
how to assign it safely that it wouldn't become NULL after a function ends?
I know it's a very basic question, but I can't find the answer in NSString reference page.
Really thanks for any help!
The behaviour is not just like C++. Objects are reference-counted. If you want to keep one around, you must place a claim on it.
If you create the object yourself with a method whose name includes the word alloc, new or copy then you have ownership already. This is like a C++ new. (When you have created an object with alloc, you need also to initialise it with some method whose name begins init. But you have to create it first. In C++ both things would be considered parts of the single act of construction.)
Objects you receive from other methods (such as two of the three NSString methods you mention) are only transiently available unless you explicitly claim ownership by calling [object retain]. You only need to do this if you want to keep them around beyond the immediate context. (There isn't really an equivalent to this in C++.)
However you gain ownership, you must relinquish it when you are finished by calling [object release]. This sort of like a C++ delete, except that the object isn't actually destroyed until all ownership claims are released.
Getting to grips with this is really really really important, perhaps the only important thing you need to know to use Objective-C. Read the object ownership documentation carefully and you'll be sorted.
I assume you're not using garbage collection? If this is the case then you need to retain the string.
NSString* str2 = [[str lastPathComponent] retain];
I suggest you do some reading on objective-c memory management.
NSString *str = #"/hi/hello";
This works because it creates a string literal. Answers to this question are worth a read to understand these in Objective-C
What's the difference between a string constant and a string literal?
In all these cases you are creating autoreleased strings. These will be deallocated when you return to the application's runloop.
NSString *str2 = [str lastPathComponent];
NSString *str3 = [NSString stringWithString:str2];
In this last one I assume you meant [[NSString alloc] initWithString:str3]
This creates a string that is retained. But this isn't a good way to create static strings.
You should create static strings in your implementation file like this
static NSString *myConstant = #"constantString"