How to determine the structure of an iOS (id) object? - ios

When I call a certain function from my iOS app, it returns an id data type. I can't see into this function so I don't know what it's doing or what the return type really is.
If I print it to console using NSLog("#"...) I get a string similar to this:
2012-01-18 19:03:08.915 HelloWorld[165:707] Key Press State.
Is there any way for me to determine the structure of this basic Id object? How would I go about getting a specific part of that response out, such as "Key press state". String parsing seems like a horrible idea, but maybe that's the only way. Perhaps the data really is just an NSString?
Thanks!

Try this:
NSLog(#"Mystery object is a %#", NSStringFromClass([mysteryObject class]));

If you look in <objc/runtime.h> you'll see methods for querying an object about its class, method selectors, ivars, etc. However, you don't usually want to do that, as it breaks encapsulation and can lead to you relying on implementation details that might change in the future, so be careful with it. You can read more here.

Related

How "id" type understands the receiver of method without casting?

After merging master to my working branch I got compiler error on the line, which wasn't be changed. The error looks like
id test;
[test count];
Multiple methods named 'count' found with mismatched result.
At first it looks clear, because compiler doesn't know which concrete type the "test" variable is. But I don't understand why it worked before.
If I create a new file this line works, assuming that is a NSArray's method. Why compiler doesn't show error in this case?
While showing error message, there is several possible receivers of count method are shown. (NSArray, NSDictionary, NSSet) Does it search all classes that can receive that message and show error if there are multiple?
I noticed that error occurs when I import "-Swift.h" file. How it depends?
Compiler doesn't cast or check your id type. It just provides you all possible selectors. You said that this issue connected with importing "-Swift.h" file. In this case check you Swift code, probably you have count function visible for Objective C which returns something else than Int.
Also, you can check the issue in Issue navigator, select it and it will show all count calls visible in Objective C. Check them all, most of them will return NSUInteger, but there should be one that returns something else, for example:
SWIFT_CLASS("_TtC3dev19YourClass")
#interface YourClass : NSObject
- (int32_t)count SWIFT_WARN_UNUSED_RESULT;
#end
Objective-C doesn't need to know the type of the receiver. At run-time, all objects are just id, and everything is dynamically dispatched. So any message can be sent to any object, no matter its type. (At run-time, objects are free to decide what to do with messages they don't understand. The most common thing to do is raise an exception and crash, but there are many kinds of objects that can handle arbitrary messages that don't map directly to method calls.)
There is a couple of technical details, however, that complicate this.
The ABI (application binary interface) defines different mechanisms for returning certain primitive types. As long as the value is "a word-sized integer," then it doesn't matter (this includes things like NSInteger and all pointers, which means by extension all objects). But on some processors, floats are returned in different registers than integers, and structs (like CGRect) might be returned in a variety of ways depending on their size. In order to write the necessary assembly language, the compiler has to know what kind of return value it will be.
ARC has added additional wrinkles that require that the compiler know a more about the type of the parameters (specifically whether they're objects or primitives), and whether there are any memory-management attributes that have to be considered.
The compiler doesn't really care what "real" type test is, as long as it can figure out the types and attributes of -count. So when dealing with an id value, it looks through every known selector it can see (i.e. every one defined in an included header or the current .m). It's fine if there are many of them on different classes, as long as they all agree. But if it can't find the selector at all, or if some of the interfaces disagree, then it can't compile the line of code.
As lobstah notes, you likely have a type somewhere in your Swift code that has an #objc method called count() or an #objc property named count that returns something other than Int (which maps to NSInteger, and so match the usual signature of -count). You'll need to fix that method, or you'll need to hide it from ObjC (for example, by adding #nonobjc).
Or much better: get rid of the id, and use its actual type. id is generally a bad idea in Cocoa, and is especially a bad idea if you're calling methods on it, since the compiler can't check that the object will respond and you may crash.

Casting method parameters in Objective-C

Update
Okay, first of all, thank you all for the huge amount of activity. It seems that I did not phrase my question too well, since many of the answers got (rightfully) stuck on the id input parameter, and following poor design patterns, but it was merely an example. I'll add some context to my question:
Suppose that there are multiple different implementations for doSomethingWithParameter:, requiring a specific instance as input parameter
My class in the example will only ever get called with an instance of SpecificClass as input parameter
With these assertions, here is my assumption: Given, that you know the type of the parameter, there is no benefit in type checking and casting, just for the sake of extra safety.
Original post
Suppose I have a general method in my protocol declaration, which takes an id input parameter:
#protocol MyProtocol <NSObject>
- (void)doSomethingWithParameter:(id)inputParameter;
#end
In a class, which conforms to MyProtocol, I usually prefer making the type of inputParameter explicit like so:
- (void)doSomethingWithParameter:(SpecificClass *)inputParameter
{
/... do something with param
}
Occasionally I received critique for choosing this solution, as opposed to the following:
- (void)doSomethingWithParameter:(id)inputParameter
{
if ([inputParameter isKindOfClass:[SpecificClass class]]) {
SpecificClass *myInstance = (SpecificClass *)inputParameter;
/... do something with param
}
}
I really prefer the first version, since it clearly states the parameter my instance is expecting. It is more concise, and clear. I generally don't think I can gain much from type checking/casting.
My question: from a coding standard standpoint, which one is the better solution? Does the first one have any disadvantages?
Update
From the update to your question, it seems that you are trying to achieve some variation of a functionality provided by the generics in modern languages.
Since Objective-C does not support this pattern, you can either sacrifice type safety, or rethink your design decisions.
If you go the first way, you should make it really clear by other means (naming, documentation) what types are you expecting. Then it might be reasonable to assume that your method will only be called with proper params.
But I would still add NSParameterAssert to simplify future debugging.
Original Answer
If you are using the first approach, you have a mismatch between declaration and definition of the method. Due to dynamic nature of obj-c (method signature does not include types of parameters), compiler does not complain about it.
However, when calling the method, only declaration is visible, so any information about the type of parameters is derived from that - all the type checking (yes, here compiler does it) is performed based on declaration.
In conclusion, to avoid confusing bugs and misuse of API, you should definitely use the second approach. Or change declaration together with definition.
Edit
Also, I can think of third solution, that somewhat merges convenience of the first approach with type safety of the second one:
- (void)doSomethingWithParameter:(SpecificClass *)inputParameter
{
NSParameterAssert([inputParameter isKindOfClass:[SpecificClass class]]);
// do something
}
First of all, when you use id for a parameter type that means either that type may vary or you may invoke method with ambiguous parameter. For both cases, second one is preferred as it checks type and prevents unwanted crash.
If you prefer the type of inputParameter explicit then simply define it in the protocol, like
#protocol MyProtocol <NSObject>
- (void)doSomethingWithParameter:(SpecificClass *)inputParameter;
#end
and for this forward declaration you may have to import module/class, like
#import "SpecificClass.h" // import class
OR
#class SpecificClass; // import module
What you do is perfectly fine. If your method is called with a parameter that is an instance of the wrong class, that is a bug in the caller. In Objective-C, you don't work around bugs, you make them crash your code, and then you fix the bug (that is why nobody handles exceptions, exceptions are bugs in your code and when they crash your code, the cause of the exception needs to be fixed).
This is much more common when you pass blocks, for example a block testing array elements, where you know exactly what type of array to expect.

Is the use of id type in method a good practice?

I am creating a set of API and some users have suggested that I use id type for a particular method that can accept custom object (defined by the API) or string instead of creating two versions. Is the use of id type in method a good or acceptable practice? Does Apple do it with their any of their API?
That would be very poor practice. If you're creating an API you need to retain full control, and allowing users to pass any object to your method at which point you would have to cast it to that object or string you mentioned could be fatal depending on what's passed. Creating two methods with different parameters is not only okay, but follows the tenets of polymorphism to the T.
Accepting id is not in itself good or bad practice. How much manual procedural if/then/else/if/then/else nonsense will you acquire? If quite a lot then something is wrong.
Put another way: if the conditional logic related to different kinds of object ends up being implicit, via the Objective-C dispatch mechanisms, then the design is good. If you end up impliedly reimplementing dynamic dispatch then you've gone completely wrong.
Apple does it frequently. Just off the top of my head there are:
as per Nikolai's comment, all the collection types: set, dictionary, array, etc.
anything that takes %# as a format specifier: NSLog, certain methods on NSString, etc.
anything that still uses an informal protocol.
anything in or semi-close to the runtime like key-value coding.
archiving and the user defaults.
anywhere that storage is offered for your own use — the hardy userInfo on NSTimer and the rest.
anywhere that target/action is used — all UIControls, the notification centre, etc.
As per my comment, suppose your custom class had this method:
- (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
And suppose it were the only method being called by whomever is being passed either a string or your custom object. Then id would be the right choice, since you'd have in effect implemented an informal protocol, and the thing being passed an object genuinely doesn't care whether it's a string or not. The only contractual requirement is the informal protocol and the protocol is informal i.e. has no footprint on the type syntax.
Conversely, suppose your custom class had no methods in common with NSString and your code just looked like:
- (void)myMethod:(id)object
{
if([object isKindOfClass:[NSString class]])
[self myMethodOnString:object];
else
[self myMethodOnCustomClass:object];
}
Then id would be inappropriate. You're just obscuring what the method does and implicitly reproducing work that's built into the runtime anyway.

UILexicon in Objective-C

How do you use UILexicon in Objective-C? I find the documentation Apple provides is extremely unhelpful.
What does it do? Does it return a dictionary or proper spellings of words? Or do I provide a word like "hellllo" and it matches it with the proper spelling "Hello" and returns that as a string?
Any help would be appreciated.
requestSupplementaryLexiconWithCompletion:
Here's my error report, but obviously I'll have errors because I'm completely guessing how to use the function, no clue what goes inside the block statement (because the docs (at the time) don't say! (Beta 4 docs)) Hahahah!
I've never used this feature, but a quick web search for "UILexicon" landed me in Apple's documentation; reading and following links from there filled in the picture pretty quick.
App Extension Programming Guide has a quick explanation of what lexicons are for:
Every custom keyboard (independent of the value of its RequestsOpenAccess key) has access to a basic autocorrection lexicon through the UILexicon class. Make use of this class, along with a lexicon of your own design, to provide suggestions and autocorrections as users are entering text.
Clicking the UILexicon link on that page took me to the reference doc for that class, which explains that it's a read-only list of Apple-provided term pairs. Each of its entries is a UILexiconEntry object -- the docs for that class say it provides a userInput (what the user typed, e.g. "ipad") and a documentText (what to substitute for it, e.g. "iPad"). Since those classes are read-only, it follows that they're probably not a way for you to provide your own autocorrection pairs -- as stated in the docs, they're for supplementing whatever autocorrection system you implement.
At this point, I don't even have to look at the doc for requestSupplementaryLexiconWithCompletion: to get a good idea how to use it: just the declaration tells me:
It's a method on UIInputViewController, the class I'd have to subclass to create a custom keyboard. Somewhere in that subclass I should probably call it on self.
Its return type is void, so I can't get a lexicon by assigning the result of a requestSupplementaryLexiconWithCompletion call to to a variable.
It calls the block I provide, passing me a UILexicon object as a parameter to that block.
It's got words like "request" and "completionHander" in it, so it'll probably do something asynchronous that takes awhile, and call that block when it's done.
So, I'm guessing that if I were writing a custom keyboard, I'd call this method early on (in viewDidLoad, perhaps) and stash the UILexicon it provides so I can refer to it later when the user is typing. Something like this:
#property UILexicon *lexicon;
- (void)viewDidLoad {
[super viewDidLoad];
[self requestSupplementaryLexiconWithCompletion:^(UILexicon *lexicon){
self.lexicon = lexicon;
}];
}
Because it's unclear how long requestSupplementaryLexiconWithCompletion will take to complete, any place where I'm using self.lexicon I should check to see if it's nil.
Back in the App Extension Programming Guide, it lists "Autocorrection and suggestion" under "Keyboard Features That iOS Users Expect", right before saying:
You can decide whether or not to implement such features; there is no dedicated API for any of the features just listed
So it sounds like autocorrection is something you have to do yourself, with your own UI that's part of the view presented by your UIInputViewController subclass. The API Quick Start for Custom Keyboards section in the programming guide seems to hint at how you'd do that: use documentContextBeforeInput to see what the user has recently typed, deleteBackward to get rid of it, and insertText: to insert a correction.

NSMutableArray and indexAtObject

I am a newbie to Objective-C and I am working on getting a good handle in working with arrays.
According to Apples own documentation NSMutableArray inherits from NSArray. I am seeking to use the method objectAtIndex:i within a for loop. Yet Xcode is claiming that
"Property 'objectAtIndex' not found on object of type
'NSMutableArray'".
Within a for loop I am performing (or seeking to) the following test:
if([self.cards objectAtIndex:i].isChosen){
do something here }
I am certain I not doing this right. It can be frustrating learning the idiosyncrasies of a new programming language. For me Objective C has, so far, borne little resemblance to C++ or C.
Any pointer or assistance would be greatly appreciated.
Thanks in advance
Walt Williams
From your error message, you could not be using the method and parameter correctly. Possibly you're trying to use a dot notation?
Your code should be like:
id object = [array objectAtIndex:index];
where the index comes from your loop and you then use the object.
In addition to Wain's answer, which is correct, but for the sake of completeness, you are using the array and trying to call the method this way:
Ex:
array.objectAtIndex.i = 5; //Java.....?
which is the cause of this error:
"Property 'objectAtIndex' not found on object of type
'NSMutableArray'".
It is complaining that you are trying to access a property named "objectAtIndex", which of course, doesn't exist.
You mean to call the method:
[array objectAtIndex:i];
In Objective-C, it is called "you are sending a message (objectAtIndex:i) to the array".
I am guessing, but hard to tell without your header file declarations, that your problem is you need to #import the file where your class is declared. This is the most common problem when having issues like this. It somehow does not know that it is an NSMutableArray. Also as I noted above you need to assign the value you get back from the array into a typed variable of that class so that you can then access the isChosen property.

Resources