Trying to understand NSLog for printing to the console in Xcode - ios

I'm trying to understand NSLog and how to print to the console in Xcode. I understand that NSLog uses what are called "tokens" to setup the type of variable being referenced to print (I think that's right?). What I need to know is the difference in which tokens to use and what they mean?
For example, after declaring an NSArray like below, I'd want to print the drink names to the console. I could do that like:
NSArray *drinks = #[#"juice", #"water", #"coffee"];
for (NSString *drinkName in drinks) {
NSLog(#"%#", drinkName);
}
So...am I using the #"%#" token because it's an NSString?
I would use #"%i" for an integer, and a #"%f" for a float? What about doubles? If anyone could shed some easy-to-understand beginner's knowledge on NSLog, that would be great! =)

As I understand it, NSLog isn't an Objective C function but a C function built into the foundation of Cocoa. Therefore it conforms to basic C functions with variadic arguments. You can use %# for all objects including NSString. This will in turn call the objects description method and print the appropriate string. Most objects have a rather useful representation already there (e.g. NSArray objects return the descriptions of all their contents)

Related

Swift collection that has defined types and copies by reference?

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.

Swift Transfer -> Array to ObjectiveC

I have problem and i cannot fix it by using google.
I have string array (written in swift). But i have objectiveC files with chart functions.
I need transfer whole swift array into objectiveC file.
Example of array:
for i in 0...11 {
mainSelectionMonthArrayValues.append(
String(WatchlistViewController().fetchDataMonth(
type:1, month: i+1, year: 2017)
)
)
}
Given you are not very specific about the code that "does not work" or the specific error you get it is hard to answer anything precisely. But given what Google finds in books for "cocoa pass swift string array to objective-c" you will have
To call an NSArray function on a Swift array you may have to cast to NSArray
so it is a pretty safe bet you will have to do this too if you want to pass mainSelectionMonthArrayValues to an Objective-C method (with an appropriate interface). Casting to an Objective-C class will not be free in most cases, but it is likely to be a constant time operation. Note however, that you will have to coerce if your object needs to be mutable on the Objective-C end. So try passing your array using something like
objCRef.callObjCMethod(mainSelectionMonthArrayValues as NSArray)
If this again "does not work" then you should provide us with more info on the kind of error you experience.

How to call a method from an object in an array?

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];

NSString decimalValue method

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.

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