EXC_AD_ACCESS on good string - macos

I log an entry using my own log function:
if(drive1SandboxBookmark)dapperLog(logWindow,100,#"Got a sandbox bookmark for %#",drive1SandboxBookmark);
I am getting Bad Exe access from this function, seemingly for no reason. It sometimes now is even doing this where the for mat string is just a string, no arguments... totally stumped:
void dapperLog(logWindowController *controller,int level,NSString *format, ...) {
if(logLevel<level)return;
va_list argumentList;
va_start(argumentList, format);
if(!format)return;
NSLog(#"%#",format);
NSMutableString * message = [[NSMutableString alloc] initWithFormat:format
arguments:argumentList];
va_end(argumentList);
if(currentRunningOSVersion<100900){
NSLog(#"%#",message);
return;
}

You get an exception if the first item in format contains a placeholder (e.g. %#) but there is no corresponding argument. Btw: You don't need mutable String at all.

Related

NSString pointer passed to function... not keeping the value I set

I'm not sure what I'm doing wrong here. I've also tried setting s1..3 in foo by using:
s1 = [[NSString alloc] initWithString:[filepaths objectAtIndex:0]];
Context below:
void foo(NSString *s1, NSString *s2, NSString *s3){
//assign long string to NSString *fps
//...
//break fps into smaller bits
NSArray *filepaths = [fps componentsSeparatedByString:#"\n"];
//the above worked! now let's assign them to the pointers
s1 = [filepaths objectAtIndex:0];
//repeat for s2 and s3
NSLog(#"%#",s1); //it worked! we're done in this function
}
int main(int argc, const char * argv[]){
NSString *s1 = nil; //s2 and s3 as well
foo(s1,s2,s3); //this should work
NSLog(#"%#",s1); //UH OH, this is null!
return 0;
}
No.
You are passing in pointers to objects which can be mutated locally. You are not changing the original objects, as you might think from plain C.
If you want to use this method (which I would not recommend - it's really odd to see in Cocoa except in the case of NSError), you would have something like:
void foo(NSString **s1, NSString **s2, NSString **s3) {
*s1 = [filepaths objectAtIndex:0]; // etc.
}
You would then pass in &s1 as the argument.
This will, of course, clobber whatever was in s1, potentially cause memory leaks, thread unsafety, etc., unless you are really careful. Which is why I say you usually won't do this.
Functions take a local copy of their arguments, so you're modifying a copy of the NSString* not the original. This is called "pass by value". What you want is "pass by reference," which looks like this:
void foo(NSString** s1) {
if(s1) *s1 = #"Different string";
}
int main(int argc, const char* argv[]){
NSString* s1 = nil;
foo(&s1);
NSLog(#"%#", s1);
return EXIT_SUCCESS;
}
It boils down to how well you understand pointers in C. It would be a good idea to read up on the "address of" operator &, and the dereference operator *, and just C pointers in general.

Check if NSString instance is contained in an NSArray

I have an array with a bunch of strings and I want to check if a certain string is contained in the array. If I use the containsObject: message on the array, I'm getting correct results. Do all NSString objects with the same string point to the same object? Or why is the containsObject: working?
NSArray *stringArray = [NSArray arrayWithObjects:#"1",#"2",#"3",anotherStringValue, nil];
if([stringArray containsObject:#"2"]){
//DO SOMETHING
}
Yes, hard-coded NSStrings (string literals) (that is any #"..." in your source code) are turned into strings that exist indefinitely while your process is running.
However NSArray's containsObject: methods calls isEqual: on its objects, hence even a dynamically created string such as [NSString stringWithFormat:#"%d", 2] would return YES in your sample snippet.
This is because NSString's isEqual: (or more precisely its isEqualToString:) method is implemented to be content aware (vs. comparing pointer identities) and thus returns YES for any pair of strings containing the very same sequence of characters (at time of comparison), no matter how and when they were created.
To check for equal (pointer-)identity you'd have to enumerate your array and compare via
NSString *yourString = #"foo";
BOOL identicalStringFound = NO;
for (NSString *someString in stringArray) {
if (someString == yourString) {
identicalStringFound = YES;
break;
}
}
(which you most likely wouldn't want, though).
Or in a more convenient fashion:
BOOL identicalStringFound = [stringArray indexOfObjectIdenticalTo:someString] != NSNotFound;
(you most likely wouldn't want this one either).
Summing up:
So the reason you're getting a positive reply from containsObject: is NOT because literal strings share the same constant instance, BUT because containsObject: by convention calls isEqual:, which is content aware.
You might want to read the (short) documentation for isEqual: from the NSObject protocol.
containsObject: performs a value check, not a pointer check. It uses the isEqual: method defined by NSObject and overridden by other objects for testing. Therefore, if two strings contain the same sequence of characters, they will be considered the same.
The distinction between pointer testing and value testing is very important in some cases. Constant strings defined in source code are combined by the compiler so that they are the same object. However, strings created dynamically are not the same object. Here is an example program which will demonstrate this:
int main(int argc, char **argv) {
NSAutoreleasePool *p = [NSAutoreleasePool new];
NSString *constantString = #"1";
NSString *constantString2 = #"1";
NSString *dynamicString = [NSString stringWithFormat:#"%i",1];
NSArray *theArray = [NSArray arrayWithObject:constantString];
if(constantString == constantString2) NSLog(#"constantString == constantString2");
else NSLog(#"constantString != constantString2");
if(constantString == dynamicString) NSLog(#"constantString == dynamicString");
else NSLog(#"constantString != dynamicString");
if([constantString isEqual:dynamicString]) NSLog(#"[constantString isEqual:dynamicString] == YES");
else NSLog(#"[constantString isEqual:dynamicString] == NO");
NSLog(#"theArray contains:\n\tconstantString: %i\n\tconstantString2: %i\n\tdynamicString: %i",
[theArray containsObject:constantString],
[theArray containsObject:constantString2],
[theArray containsObject:dynamicString]);
}
The output of this program is:
2011-04-27 17:10:54.686 a.out[41699:903] constantString == constantString2
2011-04-27 17:10:54.705 a.out[41699:903] constantString != dynamicString
2011-04-27 17:10:54.706 a.out[41699:903] [constantString isEqual:dynamicString] == YES
2011-04-27 17:10:54.706 a.out[41699:903] theArray contains:
constantString: 1
constantString2: 1
dynamicString: 1
You can use containsObject to findout if certain string is exist,
NSArray *stringArray = [NSArray arrayWithObjects:#"1",#"2",#"3",anotherStringValue, nil];
if ( [stringArray containsObject: stringToFind] ) {
// if found
} else {
// if not found
}

Customise NSLog so it shows less info

by default NSLog outputs a long string before the requested output,
e.g:
NSLog(#"Log message");
Outputs to the console:
2011-04-15 11:23:01.692 MyAppName[23160:903] Log message
I know I can add the filename and line number to the log, but how do I get rid of all the date, time and app name that appears before the message?
I find it really clutters the console in Xcode making it harder to find the information I'm after.
This is definitely the FIRST thing I do on a new project. NSLog(…) has diarrhea of the mouth. Here is a basic macro that lets you get some peace and quiet.. AND log basic objects without the annoying NSLog(#"%#", xYz); syntax (instead you just NSLog(xYz);).
#define NSLog(fmt...) NSShutUp(__PRETTY_FUNCTION__,fmt)
#define UTF8FMT(fmt,argL) \
[NSString.alloc initWithFormat:fmt arguments:argL].UTF8String
void NSShutUp(const char*func, id fmt, ...) {
if (![fmt isKindOfClass:NSString.class])
// it's not a string (aka. the formatter), so print it)
fprintf (stderr, "%s: %s\n", func,
[[NSString stringWithFormat:#"%#",fmt,nil]UTF8String]);
else { va_list argList; va_start (argList, fmt);
fprintf (stderr, "%s: %s\n", func, UTF8FMT(fmt,argList));
va_end (argList);
} }
/* SAMPLE RUN */
int main (void) { NSString *a; NSNumber *b; NSArray *c;
NSLog(a = #"Ahh, silence." );
NSLog(b = #(M_PI) );
NSLog(c = #[#"Arrays, baby!"] );
// Old syntax still works.
NSLog(#"%# * %# * %#",a,b,c);
return 0;
}
OUTPUT
int main(): Ahh, silence.
int main(): 3.141592653589793
int main(): (
"Arrays, baby!"
)
int main(): Ahh, silence. * 3.141592653589793 * (
"Arrays, baby!"
)
i would recommend that you start using a better alternatives to NSlog like SOSMAX or NSLogger.
Here is a bit overview of both of them
http://learning-ios.blogspot.com/2011/05/better-nslog-ing.html

Problem getting size of Website Xcode

When i try and compile I come up with a warning that reads initialization makes pointer from integer without a cast. No clue why. I am just trying to get the size of a website.
#import "Lockerz_RedemptionViewController.h"
#implementation Lockerz_RedemptionViewController
-(IBAction)startLoop:(id) sender {
NSData *dataNew = [NSData dataWithData:[NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]]];
NSUInteger *len = [dataNew length]; //error is here
NSLog(#"%#", len);
}
NSUInteger is just a wrapper for an unsigned int, alter your code to this (i.e. remove the * as it's not a pointer to an object)
NSUInteger len = [dataNew length];
Also I think you're going a bit overboard with your initialisation, why not just do
NSData *dataNew = [NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]];
That should return you an autoreleased object containing the data you need

calling a C function using cocoa

I have a C function with the following method signature.
NSString* md5( NSString *str )
How do I call this function, pass in a string, and save the returned string?
I tried the following, but it did not work:
NSString *temp= [[NSString alloc]initWithString:md5(password)];
thanks for your help
You're making it too hard. The stuff in []'s is effectively smalltalk. What you want is to just call the function in C:
NSString * temp = md5(password);
What is password? Is password a common "char *" pointer? Is the md5 signature you put correct?
If that's the case, you could:
NSString *temp = [[NSString alloc] initWithCString:password encoding:NSASCIIStringEncoding];
If your md5 signature is: char *md5(char *password), and you have you password stored in a NSString, you could:
NSString password = #"mypass";
char buff[128];
NSString *temp = [[NSString alloc] initWithString:password];
[temp getCString:buff maxLength:128 encoding:NSASCIIStringEncoding];
char *md5 = md5(buff);
// then you could do whatever you want with md5 var

Resources