The discussions I found about setting NSString constants made me code it the following way:
.h file:
extern NSString * const kSectionHeaders;
.m file:
NSString * const kSectionHeaders = #"header";
As the program runs, it has to test words from a text file against a series of NSString constants.
I read memory comparison should work when setting function like stated above:
if (property == kSectionHeaders) {...}
Doesn't work tough :(
The following works, but it was described as a bad solution (slower, what else?):
if ([property isEqualToString:kSectionHeaders]){...}
I feel I've done something wrong. But can't see what!
Please help :-)
Thanks!
J.
== does pointer comparison, it won't compare the values of two objects. isEqualToString: (and in general isEqual:) is the right way to do this - where was it described as a "bad solution"?
Remember variable names are just pointers to objects in memory.
The == operand compares the pointers. It will not be true unless it is comparing the exact same object in memory.
isEqualToString: is your best bet. Don't worry too much about speed. The devices are plenty fast enough to do the comparison in the blink of an eye. The things that really take noticible time are drawing on screen and reading from disk.
Who described that as a bad solution? It is the only proper/correct solution to the problem at hand.
Related
On iOS, I was wondering, say if I read user provided password value as such:
NSString* strPwd = UITextField.text;
//Check 'strPwd'
...
//How to clear out 'strPwd' from RAM?
I just don't like leaving sensitive data "dangling" in the RAM. Any idea how to zero it out?
Basically you really can't. There are bugs filed with Apple over this exact issue. Additionally there are problems with UITextField and NSString at a minimum.
To reiterate the comment in a now deleted answer by #Leo Natan:
Releasing the enclosing NSString object does not guarantee the string
bytes are zeroes in memory. Also, if a device is jailbroken, all the
sandboxing Apple promises will be of no use. However, in this case,
there is little one can do, as it is possible to swap the entire
runtime in the middle of the application running, this getting the
password from the memory.
Please file another bug with apple requesting this, the more the better.
Apple Bug Reporter
While NSString doesn't have this capability (for reasons of encapsulation mentioned elsewhere), it shouldn't be too hard to have your app use regular old C-strings, which are just pointers to memory. Once you have that pointer, it's fairly easy to clear things out when you're done.
This won't help with user-entered text-fields (which use NSString-s and we can't change them), but you can certainly keep all of your app's sensitive data in pointer-based memory.
I haven't experimented with it (I don't have a current jailbroken device), but it also might be interesting to experiment with NSMutableString -- something like:
// Code typed in browser; may need adjusting
// keep "password" in an NSMutableString
NSInteger passLength = password.length;
NSString *dummy = #"-";
while (dummy.length < passLength)
{
dummy = [dummy stringByAppendingString: #"-"];
}
NSRange fullPass = NSMakeRange(0, passLength);
[password replaceOccurancesOfString: password
withString: dummy
options: 0
range: fullPass];
NOTE: I have no idea if this does what you want; it's just something I thought of while typing my earlier answer. Even if it does work now, I guess it depends on the implementation, which is fragile (meaning: subject to breaking in the future), so shouldn't be used.
Still, might be an interesting exercise! :)
I've been developing iOs and OsX applications for several months now and it still feels like I'm doing something wrong. I try to stick to the Guidelines and I try to use the objects Apple provides as often as I can. But it seems they are making my code very hard to understand.
Example:
When I want to just "increment" a NSNumber Object (which is not mutable, but you get what I mean), I use awkward lines like this:
int value = [counter intValue];
counter = [NSNumber numberWithInt:value +1];
Is this really necessary? Are there more elegant ways (i++, inc(i), etc) to do simple things like this? Especially when you're working with coordinates it gets really frustrating and hard to work with.
When working with Objective C I feel like I'm allocating, deallocating and converting objects all the time and wasting so much of my own time and the CPU time with all those conversions. Thanks for your time, I really appreciate your answers and I'm looking forward to your tipps!
Using your example, is there any particular reason you are using NSNumber for a counter? It would be much better to use int so that you can use value++.
The key to good Objective-C code is to use objects when they make sense. Don't be afraid to use non-object data types and don't be afraid to drop down (not the best term) to C when required.
As #sosborn wrote: use objects only when it's required. But: when it's required, and you still feel wrong, simply don't. Write a macro for incrementing an NSNumber, use ARC for let the compiler do the memory management for you as efficiently as possible, etc. If you really worried about time, use C or assembly for time-critical tasks, or C++ if you want OO.
P. s.: NSNumber increment macro:
#define NSNUM_INC(n) do { n = [NSNumber numberWithInt:[n intValue] + 1]; } while (0);
You can write your category for NSNumber to implement the methods you need. For your example the file of category contains the following function:
-(NSNumber *)numberByAddingInt:(int)i
{
...
}
Include this file and then you can call it as:
counter = [counter numberByAddingInt:1];
Here are two ways to make a NSString.
NSString *sBody = [NSString stringWithString:#"Hello"]
versus
NSString *sBody = #"Hello"
Most of the time, I see it the first way. But is it also "clean" to use the second, or is it better to not use it?
Thanks and have a nice day!
They are equivalent. It's less code to use the literal (the second example), so I would just use that.
It's the same, the first way you're helping the compiler but it already does a great job optimizing string vars.
What's the difference between NSString *myString = #"Johnny Appleseed" versus NSString *myString = [NSString stringWithString: #"Johnny Appleseed"]?
Where's a good case to use either one?
The other answers here are correct. A case where you would use +stringWithString: is to obtain an immutable copy of a string which might be mutable.
In the first case, you are getting a pointer to a constant NSString. As long as your program runs, myString will be a valid pointer. In the second, you are creating an autoreleased NSString object with a constant string as a template. In that case, myString won't point to a real object anymore after the current run loop ends.
Edit: As many people have noted, the normal implementation of stringWithString: just returns a pointer to the constant string, so under normal circumstances, your two examples are exactly the same. There is a bit of a subtle difference in that Objective-C allows methods of a class to be replaced using categories and allows whole classes to be replaced with class_poseAs. In those cases, you might run into a non-default implementation of stringWithString:, which may have different semantics than you expect it to. Just because it happens to be that the default implementation does the same thing as a simple assignment doesn't mean that you should rely on subtle implementation-specific behaviour in your program - use the right case for the particular job you're trying to do.
Other than syntax and a very very minor difference in performance, nothing. The both produce the exact same pointer to the exact same object.
Use the first example. It's easier to read.
In practice, nothing. You wouldn't ever use the second form, really, unless you had some special reason to. And I can't think of any right now.
(See Carl's answer for the technical difference.)
This works -- it does compile -- but I just wanted to check if it would be considered good practice or something to be avoided?
NSString *fileName = #"image";
fileName = [fileName stringByAppendingString:#".png"];
NSLog(#"TEST : %#", fileName);
OUTPUT: TEST : image.png
Might be better written with a temporary variable:
NSString *fileName = #"image";
NSString *tempName;
tempName = [fileName stringByAppendingString:#".png"];
NSLog(#"TEST : %#", tempName);
just curious.
Internally, compilers will normally break your code up into a representation called "Single Static Assignment" where a given variable is only ever assigned one value and all statements are as simple as possible (compound elements are separated out into different lines). Your second example follows this approach.
Programmers do sometimes write like this. It is considered the clearest way of writing code since you can write all statements as basic tuples: A = B operator C. But it is normally considered too verbose for code that is "obvious", so it is an uncommon style (outside of situations where you're trying to make very cryptic code comprehensible).
Generally speaking, programmers will not be confused by your first example and it is considered acceptable where you don't need the original fileName again. However, many Obj-C programmers, encourage the following style:
NSString *fileName = [#"image" stringByAppendingString:#".png"];
NSLog(#"TEST : %#", fileName);
or even (depending on horizontal space on the line):
NSLog(#"TEST : %#", [#"image" stringByAppendingString:#".png"]);
i.e. if you only use a variable once, don't name it (just use it in place).
On a stylistic note though, if you were following the Single Static Assigment approach, you shouldn't use tempName as your variable name since it doesn't explain the role of the variable -- you'd instead use something like fileNameWithExtension. In a broader sense, I normally avoid using "temp" as a prefix since it is too easy to start naming everything "temp" (all local variables are temporary so it has little meaning).
The first line is declaring an NSString literal. It has storage that lasts the lifetime of the process, so doesn't need to be released.
The call to stringByAppendingString returns an autoreleased NSString. That should not be released either, but will last until it gets to the next autorelease pool drain.
So assigning the result of the the stringByAppendingString call back to the fileName pointer is perfectly fine in this case. In general, however, you should check what your object lifetimes are, and handle them accordingly (e.g. if fileName had been declared as a string that you own the memory to you would need to release it, so using a temp going to be necessary).
The other thing to check is if you're doing anything with fileName after this snippet - e.g. holding on to it in a instance variable - in which case your will need to retain it.
The difference is merely whether you still need the reference to the literal string or not. From the memory management POV and the object creational POV it really shouldn't matter. One thing to keep in mind though is that the second example makes it slightly easier when debugging. My preferred version would look like this:
NSString *fileName = #"image";
NSString *tempName = [fileName stringByAppendingString:#".png"];
NSLog(#"TEST : %#", tempName);
But in the end this is just a matter of preference.
I think you're right this is really down to preferred style.
Personally I like your first example, the codes not complicated and the first version is concise and easier on the eyes. Theres too much of the 'language' hiding what it's doing in the second example.
As noted memory management doesn't seem to be an issue in the examples.