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];
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 am having trouble getting an NSString property from an array. I have an NSArray composed of an object called JSMessage. Inside of this object is a string called sender I am trying to retrieve. At the moment, I am only trying to print it to the log. Here is the code I am trying:
NSLog(#"%#",[[self.messages [objectAtIndex:indexPath.row]].sender ]);
I get an error about my brackets and another one telling me objectAtIndex is an undeclared identifier.
What am I doing wrong?
It should be [self.messages objectAtIndex:indexPath.row]
From here you can see the object in a certain index.
To get the JSMessage object:
JSMessage *jSMessageObj = [self.messages objectAtIndex:indexPath.row];
Well, think about it. Your code contains the phrase [objectAtIndex. But a method name can never be the first thing in square brackets. The first thing in square brackets must be the receiver to which the message is being sent.
For example, you say [myString lowercaseString]. You don't say [lowercaseString]. But that is what you are saying.
Besides removing all of the extra brackets, you can also do the following:
NSLog(#"%#", [self.messages[indexPath.row] sender]);
This uses the modern syntax for accessing elements from an NSArray. And since the array access returns an object of type id, you can't directly access the object's property using property syntax. You need to call the getter method as I show above.
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.
EDIT: This is for a static NSMutableDictionary, not a class member. I see a lot of answers thinking it's a class member, sorry about that. Thanks for the quick responses.
I had a working piece of code, but it was used 2x, so I wrote a convenience function:
- (void) initializeDictionary:(NSMutableDictionary*)mutableDict fromPathName:(NSString*)path
{
mutableDict = // get from file, init if not there.
}
Then I noticed nothing was saving. Stepping through the code, the function creates a dictionary, but the "passed in" value is still nil upon returning. This completely blows away my understanding of pointers (don't take this the wrong way, I'm very comfortable). But their purpose, I thought, was to let you "pass the address" so I could hydrate the thing it points to.
since mutableDict is the "value copy", ARC appears to be wiping it out. I'm asking here because
the other questions have the opposite question "Why is this being retained?" and
It doesn't seem right. Your very first tutorial in any pointer language is "so you can mess with the original value". Does Arc really do this? Is there a concept I'm not getting?
And of course, I may be overlooking something simple.
I seem to have solved it by doing the writeback trick I found elsewhere:
- (void) initializeDictionary:(NSMutableDictionary* __strong *)mutableDictionary fromPath:(NSString*)path;
and using the & to call and * deref to change the value. It just seems far less than elegant.
You pass a pointer of NSDictionary. If you change the pointer value of mutableDict (by allocation or assignment) this will NOT be reflected on the variable passed as this function's argument and since mutableDict is locally defined in the function it will be released by ARC automatically. (Do not forget that even for pointers the address is copied when passed to a function).
The reason that your second solution works is because you pass a pointer to a pointer (not sure if strong is needed there). The value of *mutableDict will be reflected outside since you update a value in the address pointed to by mutableDict. This value is the address of your allocated dictionary and since you change it via a pointer it will be reflected outside the function.
To do what you want you use multiple indirection
- (void) initializeDictionary:(NSMutableDictionary**)mutableDict fromPathName:(NSString*)path
{
mutableDict = // get from file, init if not there.
}
Notice the parameter is (NSMutableDictionary **), a pointer to a pointer to an NSMutableDictionary.
To call the method you need to create a pointer and pass it in like this:
NSMutableDictionary *mutableDictionary;
[self initializeDictionary: &mutableDictionary fromPathName:...];
And on return, the mutableDictionary variable will have been initialised by the method.
However, It's probably easier to just return the dictionary from the method rather than doing it this way.
In Objective-C if you define a variable in a method, the variable is visible only in that method.
If you need to save the variable to use it outside, you need to "return" it from your function. What you did wasn't a trick, is another way of returning a variable.
Try with:
- (void)initializeDictionaryFromPath:(NSString*)path {
NSMutableDictionary *mutableDict = // your initialization
return mutableDict;
}
Then you can define your dictionary with this code:
NSMutableDictionary *mutableDict = [self initializeDictionaryFromPath:path];
In cocos2d the action CCCallFunND allows you to call a selector and send it data, but the selector I want to call takes both an NSString and a float. Can you send more than one datum and if so, what is the syntax.
I know I can make a new object class that contains both types and pass that, but that seems kludgy to me. The whole idea of the method I'm calling is to cut down the overhead, and doing it with a new class seems like it defeats the purpose.
Here's the syntax...
CCCallFuncND actionWithTarget:(id) selector:(SEL) data:(void *)
I think the best solution will be to create a structure with your string and float. But you can also create a NSArray and put your NSString and NSNumber to it. Then just pass the array.