I ran into some old code in a project and I do not understand why it does not crash.
Data is being ingested from an HTTP request to an API and certain values are returned as strings, but treated as NSNumbers. For example, in the code below, value is an NSString.
[NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *)value decimalValue]]
However, it is incorrectly cast to an NSNumber and the decimalValue method of NSNumber is called. There is no decimalValue method for NSString, so I don't understand why the line above does not cause a crash. When I inspect value with [value isKindOfClass:[NSString class]] and [value isKindOfClass:[NSNumber class]] I get the expected results.
Is there a behavior of the runtime I do not understand? Does a cast cause the methods of a different class to be used?
edit
I'm updating the title of this question to more accurately reflect the issue. The answer was that NSString has a private decimalValue method that was causing the confusion.
The real answer? Voodoo. Not really, but sort of.
Lots of classes in cocoa have private methods behind the scenes. Usually, these methods go mostly unnoticed, and even more rarely used.
However, if you grab their implementation using class_getMethodImplementation and use dladdr to print the location of the symbol as such:
IMP theImp = class_getMethodImplementation([NSString class], #selector(decimalValue));
Dl_info info;
dladdr(theImp, &info);
puts(info.dli_sname);
You should see something like -[NSString(NSDecimalExtension) decimalValue].
My best guess is that this method is used for KVO & friends when marshaling values to/from decimal values, or it's used with NSNumberFormatter.
Related
In converting old projects from Objective-C to Swift, I've mostly been able to use Dictionary in place of NSMutableDictionary. But in some cases, it's a hassle or uses a lot of extra memory to have the Dictionaries copying by value.
I thought I could simply change some Dictionary collections to NSMutableDictionary to make them objects that copy by value, but I don't see a way to specify the key and value types. This works in Objective-C:
NSMutableDictionary<NSString*, NSString*> *dict = [NSMutableDictionary dictionary];
But this gives an error "Cannot specialize non-generic type 'NSMutableDictionary'" in Swift:
let dict: NSMutableDictionary<String, String> = [:]
Is there a way to specify the types so I don't have to be constantly casting the values I get out of the dictionary?
Alternatively, is there another kind of collection object that supports key and value types like Dictionary but copies by reference like NSMutableDictionary?
UPDATE
I tried using NSMapTable as suggested in the comment below. That's missing some features of NSDictionary (e.g., it doesn't conform to IteratorProtocol), so I made a subclass to try making a drop-in replacement for Dictionary. I then ran into problems making my subclass generic since Swift and Objective-C have different support for that.
Since that would either require a lot of casting (or making a different subclass for each type of data I wanted to store in the dictionary), I then tried just using NSMutableDictionary and casting all the values when I read them. Unfortunately, after all that work, I couldn't see any difference in memory usage compared to using Dictionary.
So I guess having collections that copy by value isn't really my problem. It shouldn't be since I'm never retaining anything for very long, but I didn't see these memory problems until I migrated from Objective-C. I'll have to do more testing and explore other solutions.
The Objective-C specification:
NSMutableDictionary<NSString*, NSString*>
Is not a true implementation of Generics. It simply gives hints to the compiler that the dictionary will contain strings as the keys and values and the compiler can tell you, at compile time, if you make a simple mistake.
At the end of the day, there is nothing at runtime to enforce those type specifications. An NSDictionary (mutable or not) will have id for the keys, and id for the values and the runtime format will not change. That's why you can get away with using [NSMutableDictionary dictionary] to initialize all NSDictionaries... the type spec only has meaning at compile time.
In contrast when you use a identical syntax in Swift, say Dictionary<String, Int>, you are using true generics. The runtime representation of the dictionary may change depending on what key and value types you use.
In other words, in spite of similarities in their in Syntax, the <type, type> construct in Objective-C and in Swift mean VERY different things.
In Swift's Eyes, an NSDictionary (mutable or not) is simply a NSObject, just like every other NSObject so NSDictionary<NSString, NSString> is a nonsensical use of the generic type specification syntax - you're saying you want to use generics with a type that is not a generic type (hence the error).
There is no Swift syntax (that I'm aware of) that lets you specify the type you'd like to stand in for NSObject in things like NSDictionaries and NSArrays. You're going to have to use casting.
Even in Objective-C the type specs mean nothing and it's possible to squeeze something in there that doesn't belong. Consider:
NSDictionary<NSString *, NSString *> *myDictionary = [NSMutableDictionary dictionary];
((NSMutableDictionary *)myDictionary)[[NSNumber numberWithInt: 3]] = [NSURL URLWithString: #"http://www.apple.com"];
Here I declare the dict to use Strings, then shove in a number and a URL. The only way to guard against this would be to check the types, that is to do typecasting (or at least type-checking), for each key and value. Most folks code doesn't do that because it would be a pain, but the only way to get true safety.
Swift, in contrast, focus on the safety right up front. It's one of the defining differences between Swift an Objective-C. So you have to go through the pain if you insist on using "unsafe" Objective-C types.
I know Apple has cautioned against using it. But given their reasoning, the results are far from relevant and expected.
Here is my debug output - the results aren't different in code - below is just for brevity:
(lldb) po [#"Hello" isKindOfClass:[NSMutableString class]]
true => A mutable string?
(lldb) po [[#"Hello" mutableCopy] isKindOfClass:[NSMutableString class]]
0x00000001019f3201 => What's that?
(lldb) po [[#"Hello" mutableCopy] isMemberOfClass:[NSMutableString class]]
0x000000010214e400 => What's that?
(lldb) po [#"Hello" isMemberOfClass:[NSMutableString class]]
false => Once again?
Further to that, I removed all the string literal code and tested the following:
NSMutableString * m = [[NSMutableString alloc] initWithString:#"Hello"];
bool b = [m isKindOfClass:[NSMutableString class]];
NSLog(#"%d", b); --> 1 Expected.
b = [m isKindOfClass:[NSString class]];
NSLog(#"%d", b); --> 1 Expected.
b = [m isMemberOfClass:[NSString class]];
NSLog(#"%d", b); --> 0 Expected.
b = [m isMemberOfClass:[NSMutableString class]];
NSLog(#"%d", b); --> 0 Not Expected.
Is there an enlightenment?
UPDATE:
Apple's own take:
Be careful when using this method on objects represented by a class
cluster. Because of the nature of class clusters, the object you get
back may not always be the type you expected. If you call a method
that returns a class cluster, the exact type returned by the method is
the best indicator of what you can do with that object.
Why not simply say do not employ isKindOfClass and isMemberOfClass with cluster classes?
The explanation prevents use from the perspective such as:
You might end up modifying something that you are not supposed to.
instead of stating:
These methods do not work with class clusters.
(in the examples, I have shown above - I am clearly passing correct objects and still not getting expected results.)
UPDATE 2:
Filed with Apple Radar.
The methods don't "mislead" as you claim in the comments. Because NSString and NSMutableString are class clusters, they can return an instance of any concrete subclass that is-a NSString or NSMutableString, respectively.
As it happens, most concrete subclasses in the NSString cluster are also subclasses of NSMutableString. Instead of using the actual class to control mutability, they use a flag or something like that. All perfectly valid and complying with the design contract.
So, that's why [#"Hello" isKindOfClass:[NSMutableString class]] returns true. You ask "A mutable string?" No. That expression is not a valid test of mutability. As documented, there is no valid test of mutability. This is at the core of your misunderstanding. You must not attempt to interrogate the class of an object to determine if it's mutable. You must respect the static type of the pointer in the API.
Edit: This is documented in Concepts in Objective-C Programming: Object Mutability – Receiving Mutable Objects:
Use Return Type, Not Introspection
To determine whether it can change a received object, the receiver of
a message must rely on the formal type of the return value. If it
receives, for example, an array object typed as immutable, it should
not attempt to mutate it. It is not an acceptable programming practice
to determine if an object is mutable based on its class membership—for
example:
if ( [anArray isKindOfClass:[NSMutableArray class]] ) {
// add, remove objects from anArray
}
For reasons related to implementation, what isKindOfClass: returns in
this case may not be accurate. But for reasons other than this, you
should not make assumptions about whether an object is mutable based
on class membership. Your decision should be guided solely by what the
signature of the method vending the object says about its mutability.
If you are not sure whether an object is mutable or immutable, assume
it’s immutable.
A couple of examples might help clarify why this guideline is
important:
You read a property list from a file. When the Foundation framework processes the list, it notices that various subsets of the
property list are identical, so it creates a set of objects that it
shares among all those subsets. Afterward you look at the created
property list objects and decide to mutate one subset. Suddenly, and
without being aware of it, you’ve changed the tree in multiple places.
You ask NSView for its subviews (with the subviews method) and it returns an object that is declared to be an NSArray but which could be
an NSMutableArray internally. Then you pass that array to some other
code that, through introspection, determines it to be mutable and
changes it. By changing this array, the code is mutating internal data
structures of the NSView class.
So don’t make an assumption about object mutability based on what
introspection tells you about an object. Treat objects as mutable or
not based on what you are handed at the API boundaries (that is, based
on the return type). If you need to unambiguously mark an object as
mutable or immutable when you pass it to clients, pass that
information as a flag along with the object.
As others have mentioned, -isMemberOfClass: tests for being an instance of that exact class and not any subclass. For a class cluster, that will always return false, because the public class is abstract and will never have instances.
The other weird results are probably because you're using po (short for "print object") for non-object values. Use the p command for boolean expressions.
tl;dr Don't use introspection in your code to determine mutability or to vary behavior (outside of extremely limited situations). Do use static types and strongly defined data structures (including strongly defined plist structures).
The introspection functionality offered by the Objective-C runtime, of which the introspection methods on NSObject are implemented against, are neither misleading nor are they returning incorrect results.
They are revealing specific details of how the various objects are implemented. This may appear quite different from what is declared in the header files.
Or, to put it another way: compile time vs. run time may be very different.
That is both correct and OK. And confusing.
Objective-C implements a strong notion of duck typing. That is, if you have a reference to something declared as NSString*, it really doesn't matter what that reference really points to as long is it responds to the API contract declared for the class NSString.
The confusion comes from trying to treat Objective-C as a fully dynamic, introspection driven, language. It wasn't designed for that (well, it kinda was, but that notion was dropped by about 1990) and, over time, strong typing has become more and more the norm. I.e. let the compiler figure out if something is valid and don't try to second guess at runtime.
I'm new to Objective-c and I think this should be really easy but somehow I can't figure it out.
I need to call a method from an object which is stored in an NSArray.
The Java code would be: myArray[0].getMyValue();
So I read on the internet that in Objective-c it should look like this: [[myArray objectAtIndex: 0] getMyValue]. Unfortunately this didn't work.
I found other solutions on the internet but none of them worked.
So I hope you guys could help me out?
Update
I'm getting these two error messages:
No known instance method for selector 'getMyValue'
Sending 'id' to parameter of incompatible type 'CGFloat' (aka 'double')
This doesn't work because Objective-C doesn't know what is the type of the object in the array.
Luckily, Apple has added lightweight generics in Xcode 7, which allow you to create typed arrays. This will of course work only if you intend to have one type of object in the array. The syntax looks like this:
NSArray<NSString *> *stringArray;
If you plan to have objects with different types in the array, then you need to cast the object to your type, to be able to call your method. That would look like this:
[((YourObject *)[myArray objectAtIndex: 0]) getMyValue];
And as #Michael pointed out in the comment, another and nicer way to do this would be:
[((YourObject *)myArray[0]) getMyValue];
Objects are stored with id type in NSArray, so you can cast this object to the object type you want. For instance :
NSNumber *myNumber = (NSNumber *)[NSArray objectAtIndex:0];
[myNumber myMethod];
I have a question about the parameter that #synchronized take, I have read the Apple document about synchronisation but still I don't have a clear idea.
I have a case that #synchronized will take a string property inside some object like this :
#synchronized(someObject.A)
since A is a NSString object and in some cases will carry the same value but from different someObject is this will guarantee the locking for all objects with same A values?
It's worth noting that NSString has some special cases that are handled magically.
NSString *s1 = #"Test string";
NSString *s2 = #"Test string";
Here, s1 and s2 are actually compiled to access the same memory address, even though they are different variables and could be instantiated in completely different places within the application.
However, if you are loading the data on-the-fly or using one of the construction methods for NSString instead of hard-coding it, strings that match character-for-character will not share the same memory.
You can consider this the difference between comparing with == and isEqualToString:. #synchronized only ever uses the == result.
So, to answer your question: maybe.
If you are using hard-coded values of the form #"some string" within your application, your #synchronized command will link to all objects that share the same textual value for A.
If you are creating NSString objects by any other means, your #synchronized command will only link to objects that point to the exact same NSString object.
The synchronization will be done on whatever object someObject.A is currently referencing. The important piece is the actual object you use #synchronized on.
If you assign the same string to two completely difference properties and you then use #synchronized on those two completely different properties, it will work since both point to the same string.
The following example may help:
// In one method
#synchronized(someObject.A) {
}
// In another method
NSString *foo = someObject.A;
#synchronized(foo) {
}
The above two blocks will be thread safe on the same string object.
It looks to me like sending setString: to a NSMutableString that hasn't had init called on it yet does not call init on it's own. For example:
NSMutableString *string; // Declare, but do not init yet
[string setString:#"foo"];
NSLog (#"%#",string); // Prints "(null)"
I'd like to overwrite this behavior, so that essentially
- (void) setString:(NSString *)aString
{
if (!self)
{
self = [self initWithString:aString];
}
else
{
[super setString:aString];
}
}
I could do so with a subclass, but I would have to go through my project and replace all my NSMutableStrings with my subclass, which is a pain. I was looking at the Apple Docs and it looks like what I want to do is create a Category for NSMutableString. I haven't used categories before, so I got a couple questions:
First, it looks like categories only allow me to add new methods, it doesn't allow me to overwrite existing methods. I suppose it is easy enough to just create a setStringWithInit: method that does what I want, so this first issue isn't really an issue after all (although I still have to do a find-replace through my project for setString, but oh well).
Second, and more importantly, how do I check if the sender of my new method is nil? If setString: returned something other than void, I think I could get it to work, but that's not the case here. How do I get the if (!self) check from my code above to work with a category?
Or are categories not the way to go for this kind of thing, and I'd just be better off sub-classing after all?
EDIT:
So the strings I'm using this on will actually be #propertys of a custom NSObject subclass. In my code, I'll actually be calling [myObject.someProperty setString:#"foo"];, and the debugger is showing me that someProperty is nil when I try to do this. Also, in other parts of my app I'm checking if (!myObject.someProperty) to see if that property has been used yet, so I don't want to just automatically self.someProperty = [[NSMutableString alloc] init]; in the init method of myObject's class.
Although now that I think about it, I think I can get away with replacing if (!myObject.someProperty) with if ([myObject.someProperty length] == 0), which would allow me to go through and alloc-init things right away. But if I'm initializing everything right away, that will create some memory space for it, correct? It's probably negligible though. Hm, perhaps this is what I should be doing instead.
The proper code would simply be:
NSMutableString *string = [NSMutableString string];
[string setString:#"foo"];
NSLog (#"%#",string);
Why would you not initialize the variable? There is no need to override setString: or any other method. Don't try to treat NSMutableString any differently than any other class.
Besides, overriding setString: still won't solve anything. As long as the pointer is nil you can't call a method on it.
You are marching down a path to madness. Abandon hope, all ye who enter here!
Do not try to change the language semantics so that sending a message to a nil object somehow magically creates an instance of the object. That is not how the language works.
What you are trying to do is likely impossible, and if you were able to succeed, you would create programs that are fundamentally incompatible with standard Objective-C. You might as well found a new language, Objective-D
It is legal to send a message to a nil object in Objective C. The result is that the message gets silently dropped, and nothing happens. In many other object-oriented other languages, sending a message to a nil object/zero pointer causes a crash.
The semantics of of Objective C object creation are:
First allocate memory for the object using the class method alloc:
NSMutableString* aString = [NSMutableString alloc];
Then send the newly created object an init method to set it to its initial state:
aString = [aString init];
These 2 steps are just about always combined into a single line:
NSMutableString* aString = [[NSMutableString alloc] init];
Classes sometimes include shortcut "convenience" methods that do the 2 step alloc/init for you, and return an object in one call, e.g.:
NSMutableString *aString = [NSMutableString stringWithCapacity: 50];
Do not try to fight this convention. Learn to follow it. If you cannot tolerate this convention, program in a different language. Really.
You can reimplement a method without subclassing by using method swizzling. Here's a tutorial. There are 2 reasons not to do it here though.
it would be against the good Objective-C practices, since your
setter will also be an init method. Not good.
As #rmaddy correctly points out, calling setString: on a nil object will do
nothing. Even if you do override the method.
So I recommend creating a category on NSMutableString, and implementing [NSMutableString initWithString:] there. It is a much cleaner solution.
You cannot really do that - you have a method which can be called only on instance of this object, so you will have to create it first anyways to use it.
In your code it will be "nil" anyways - it won't create itself.
Why are you doing it instead of just:
NSMutableString *string = #foo";
I cannot imagine a reason to avoid allocating an object
macros FTW!
#define setString(X,Y) if(!X){X=[[NSMutableString alloc] initWithString:Y];}else{[X setString:Y];}
When I try to assign a value with this:
It will always be initialized first
It won't be initialized until I try to give it a value
It doesn't clutter up my code
It still gives a warning if X isn't an NSMutableString, or if Y isn't an NSString or NSMutableString
I haven't tested for if Y is nil, but I expect it will cause a crash, which is what I want.
Drawbacks:
I still have to remember to always use my setString() instead of the stock setString:
I'll have to do something similar for any other setters I call (the only one that I'm worried about off hand is setValue:forKey:, which I use extensively - one step at a time I guess) - a one size fits all solution would have been nice - maybe a topic for another question.
Whatever I pass in has to be a NSString before I pass it, I cannot convert it to a string in line - but at least I get a build error if I try to do so, so it isn't up to me to remember to do so (still adds clutter though)
NSMutableString *X;
int y = 0;
setString(X, [NSString stringWithFormat:#"%d",y]) // <--- Doesn't work
NSString *Y = [NSStirng stringWithFormat:#"%d",y];
setString(X,Y) // <--- Does work