So I'm working on a simple calculator program to get used to cocoa and objective C. I've redone the whole thing multiple times and every time I finish coding, the first time i build it, it works fine, but every time after that the window wont launch and it gives me these errors:
2013-01-11 10:32:14.760 Visual Caluclator Fix[39892:403] *** Assertion failure in -[NSTextFieldCell _objectValue:forString:errorDescription:], /SourceCache/AppKit/AppKit-1138.47/AppKit.subproj/NSCell.m:1564
2013-01-11 10:32:14.762 Visual Caluclator Fix[39892:403] Ignoring exception raised in __-[NSPersistentUIManager restoreAllPersistentStateWithTalagentWindows:registeringAsReadyWhenDone:completionHandler:]_block_invoke_3: Invalid parameter not satisfying: aString != nil
I've concluded that the problem lies in my textEdited method, because when I comment the code inside of it out, the program has no issues running; however, I have no idea why this is, or why it would run the first time and not any subsequent times. When I put in an exception breakpoint, it points me to the one line in the updateUI method and the call to [self updateUI] in the textEdited method. The following code is the textEdited method and the other methods it references.(I'm fairly sure there's nothing wrong with the solve method because I used it in a command prompt calculator and it worked great. Also, I know this is a pretty convoluted way to program a calculator, with the strings and everything, but I was just trying to integrate the code I already had for the command prompt program into a cocoa program.)
In the AppDelegate class:
- (void)updateUI{
[self.calculationView setStringValue: self.calculation.calcString];//Exception breakpoint points here
}
- (IBAction)textEdited:(id)sender {
self.calculation.calcString = self.calculationView.stringValue;
[self.calculation solve];
[self updateUI];//Exception breakpoint points here
}
In the Calculation class:
- (NSString*)solve{
for (int i = 0; i < [self.calcString length]; i++) {
NSRange nextChar = NSMakeRange(i, 1);
if ([[self.calcString substringWithRange: nextChar] isEqualToString: #"*"]||
[[self.calcString substringWithRange: nextChar] isEqualToString: #"/"])
[self calcTerm: i];
}
for (int i = 0; i < [self.calcString length]; i++) {
NSRange nextChar = NSMakeRange(i, 1);
if ([[self.calcString substringWithRange: nextChar] isEqualToString: #"+"]||
[[self.calcString substringWithRange: nextChar] isEqualToString: #"-"])
[self calcTerm: i];
}
return self.calcString;
}
This might help with your problem:
http://www.raywenderlich.com/10505/my-app-crashed-now-what-part-2
The site explains how assertion errors work and demonstrate how you would fix an error like that.
It sounds to me like something in calcTerm: is setting calcString to nil. Thus, when you retrieve it later to set the field's string value to it, you end up setting the field's string value to nil, which it doesn't like.
You can check this by logging the value of calcString just before the end of solve. Then, start stringing up log statements and/or breakpoints in calcTerm: to find out how you're swapping out your string for nil.
Related
Hey this may be a stupid question but I couldn't find the answer anywhere, apologies if the answer is easily found and if my research skills are pants.
anyway is it possible to generate a crash report when an app doesn't crash? so say if a user encounters a bug could there be an option to allow them to generate a crash report which can then be sent to me? also how would I go about doing this?
Thanks for any help :)
I have used it couple of times when I had to print stack trace:
+ (NSArray *)backtrace
{
void* callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
int i;
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (
i = UncaughtExceptionHandlerSkipAddressCount;
i < UncaughtExceptionHandlerSkipAddressCount +
UncaughtExceptionHandlerReportAddressCount;
i++)
{
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
"When an application crashes on the iPhone, it disappears without telling the user what happened. However, it is possible to add exception and signal handling to your applications so that an error message can be displayed to the user or you can save changes. It is even possible to try to recover from this situation without crashing at all."
Look at http://cocoawithlove.com/2010/05/handling-unhandled-exceptions-and.html
Here's what I use for my stacktraces:
NSArray *callStackArray = [exception callStackReturnAddresses];
int frameCount = [callStackArray count];
void *backtraceFrames[frameCount];
for (int i=0; i < frameCount; i++) {
backtraceFrames[i] = (void *)[[callStackArray objectAtIndex:i] unsignedIntegerValue];
}
char **strs = backtrace_symbols(backtraceFrames, frameCount);
NSMutableArray *backtraceArray = [NSMutableArray arrayWithCapacity:frameCount];
for (int i = 0; i < frameCount; i++) {
NSString *entry = [NSString stringWithUTF8String:strs[i]];
[backtraceArray addObject:entry];
}
free(strs);
You just have to make sure you don't do any harm to your app: http://landonf.bikemonkey.org/2011/09/14. You could also use PLCrashReporter to handle all your crashes or if you're lazy like me, use a service like Crittercism
I used to use backtrace_symbols for handling my crashes too, but then I found out that it could be dangerous since the method is not async-safe. I've since looked at a bunch of crash reporting solutions and went with Crittercism for my apps and it's been pretty sweet!
I suggest you check out TestFlight SDK released a few days ago. It has some awesome features like remote logging and even live crash reports.
For an ad hoc version, you could just call the function abort(), or throw and exception of some kind.
An App Store version will not be allowed to ship if it crashes at all during testing.
please look at this code:
-(NSString *) myfun:(NSString *)name paramnoone:(int)a paramnotwo:(int)b {
static int numberofcall=0;
if(a>b) {
return name;
}
NSString *secondname = [[NSString alloc]init];
secondname = [name StringByAppendingString:#"test"];
numberofcall++;
return secondname;
}
i have a problem on it, when my code is on "return secondname" next step is going to "return name" on if statement part, im confusing a lot , because c++ does not have this problem,
please help me on solve it,
thanks for ur help and sorry for my bad English.
(Until the question is explained further I can't really answer the question, but still have valuable infos which justify being posted as an answer, so here it goes.)
In the line:
NSString *secondname = [[NSString alloc]init];
You allocate an empty string. But in the very next line:
secondname = [name StringByAppendingString:#"test"];
You overwrite the pointer secondname to the previously allocated empty string, thus creating a memory leak. Since you do not use the empty string at all, remove the first line and turn the second line into:
NSString *secondname = [name StringByAppendingString:#"test"];
Edit: Based on comments to the questions, I think what you're asking is this (correct me if I'm wrong):
You are debugging the method.
While stepping through the method with the debugger, the flow proceeds normally through the method.
But after the numberofcall++; line, the debugger suddenly jumps to the return name; instead of the return secondname; line.
If that's what's happening to you: this is normal behavior, unfortunately. When the debugger reaches a return statement the marker always "jumps" to the first return statement in the method. But even though it doesn't look that way, your return secondname; statement is really executed.
I would like to define the following function in Objective-C. I have provided pseudo-code to help illustrate what I'm trying to do.
PSEUDOCODE:
function Foo(param) {
string temp;
if(param == 1) then
temp = "x";
else if(param == 2) then
temp = "y";
else if(param == 3) then
temp = "z";
else
temp = "default";
end if
return temp;
}
For some reason if I do this... the variable who I assign it to results in a "BAD Access" error.
I don't know what the difference between:
static NSstring *xx;
or the non-static:
NSString *xx;
declarations are, and how or why I would want to use one over the other.
I also do not fully understand the initializers of NSString, and how they differ. For example:
[[NSString alloc] initWithString:#"etc etc" ];
or the simple assignment:
var = #""
or even:
var = [NSString stringWithString:#"etc etc"];
Can you give me a hand please?
So far, using the NSString value returned from functions like those listed above, always causes an error.
static NSstring *xx;
That declares a statically allocated variable, much like it does in C.
NSstring *xx;
Inside a method that declares a normal local stack variable, just as it does in C.
As you should be aware, the difference between the two is that the first will keep its value between invocations of the function (and can cause trouble if the function is called from multiple threads).
[[NSString alloc] initWithString:#"etc etc"]
That creates a new NSString object, with the contents etc etc. This may or may not be the same as any other NSString object in your program with the same contents, but you don't have to care. Memory management wise, you own it, so you are responsible for ensuring that you eventually call release or autorelease on it to avoid leaking memory.
#"etc etc"
[NSString stringWithString:#"etc etc"]
Those are basically the same. Both give you an NSString object with the contents etc etc. This may or may not be the same as any other NSString object in your program with the same contents, but you don't have to care. Memory management wise, you do not own it, so you must not call release or autorelease on the object unless you first took ownership by calling retain. Also, since you do not own it, you can use it within your method, pass it as a parameter to other methods, and even use it as the return value from your method, but you may not store it in an ivar or static variable without taking ownership by calling retain or making a copy (with copy).
Also, note that "" and #"" are very different. The first gives you a const char * exactly as it does in C, while the second gives you an NSString object. Your program will crash if you use a const char * where the code expects an NSString object.
You can do it this way:
- (NSString *)functionName:(int)param {
NSString *result = nil;
switch (param) {
case 1:
result = [NSString stringWithString:#"x"];
break;
case 2:
result = [NSString stringWithString:#"y"];
break;
case 3:
result = [NSString stringWithString:#"z"];
break;
default:
result = [NSString stringWithString:#"defaultv"];
break;
}
return result;
}
Post real code, not pseudo code, as it makes it much easier to answer your question in concrete terms.
Given that you indicate that you are quite new to Objective-C, I would suggest starting with the language guide and then moving on to the memory management guide.
Based on this SO question asked a few hours ago, I have decided to implement a swizzled method that will allow me to take a formatted NSString as the format arg into stringWithFormat, and have it not break when omitting one of the numbered arg references (%1$#, %2$#)
I have it working, but this is the first copy, and seeing as this method is going to be potentially called hundreds of thousands of times per app run, I need to bounce this off of some experts to see if this method has any red flags, major performance hits, or optimizations
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {
if (format != nil) {
va_list args;
va_start(args, format);
// $# is an ordered variable (%1$#, %2$#...)
if ([format rangeOfString:#"$#"].location == NSNotFound) {
//call apples method
NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
va_end(args);
return s;
}
NSMutableArray *newArgs = [NSMutableArray arrayWithCapacity:NUMARGS(args)];
id arg = nil;
int i = 1;
while (arg = va_arg(args, id)) {
NSString *f = [NSString stringWithFormat:#"%%%d\$\#", i];
i++;
if ([format rangeOfString:f].location == NSNotFound) continue;
else [newArgs addObject:arg];
}
va_end(args);
char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
[newArgs getObjects:(id *)newArgList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
free(newArgList);
return result;
}
return nil;
}
The basic algorithm is:
search the format string for the %1$#, %2$# variables by searching for %#
if not found, call the normal stringWithFormat and return
else, loop over the args
if the format has a position variable (%i$#) for position i, add the arg to the new arg array
else, don't add the arg
take the new arg array, convert it back into a va_list, and call initWithFormat:arguments: to get the correct string.
The idea is that I would run all [NSString stringWithFormat:] calls through this method instead.
This might seem unnecessary to many, but click on to the referenced SO question (first line) to see examples of why I need to do this.
Ideas? Thoughts? Better implementations? Better Solutions?
Whoa there!
Instead of screwing with a core method that you very probably will introduce subtle bugs into, instead just turn on "Static Analyzer" in your project options, and it will run every build - if you get the arguments wrong it will issue a compiler warning for you.
I appreciate your desire to make the application more robust but I think it very likely that re-writing this method will more likely break your application than save it.
How about defining your own interim method instead of using format specifiers and stringWithFormat:? For example, you could define your own method replaceIndexPoints: to look for ($1) instead of %1$#. You would then format your string and insert translated replacements independently. This method could also take an array of strings, with NSNull or empty strings at the indexes that don't exist in the “untranslated” string.
Your method could look like this (if it were a category method for NSMutableString):
- (void) replaceIndexPointsWithStrings:(NSArray *) replacements
{
// 1. look for largest index in "self".
// 2. loop from the beginning to the largest index, replacing each
// index with corresponding string from replacements array.
}
Here's a few issues that I see with your current implementation (at a glance):
The __VA_ARGS__ thingy explained in the comments.
When you use while (arg = va_arg(args, id)), you are assuming that the arguments are nil terminated (such as for arrayWithObjects:), but with stringWithFormat: this is not a requirement.
I don't think you're required to escape the $ and # in your string format in your arg-loop.
I'm not sure this would work well if uaStringWithFormat: was passed something larger than a pointer (i.e. long long if pointers are 32-bit). This may only be an issue if your translations also require inserting unlocalised numbers of long long magnitude.
I want my app to do something when the last NSLog has a certain string. I thought I could realize this with an if-query and isEqualtoString, but how can I make this?
Sorry for my bad English ;)
Maybe I don't understand what you're trying to do, but you can just create the string somewhere, log it, and then test it:
NSInteger _someInt = 2;
NSString *_someString = #"bananas";
NSString *_stringToBeLogged = [NSString stringWithFormat:#"%d %#", _someInt, _someString];
NSLog(#"%#", _stringToBeLogged);
if ([_stringToBeLogged isEqualToString:#"2 bananas"]) {
NSLog(#"I logged two bananas...");
}
You could consider creating your own custom log function which calls NSLog() after checking for your string constant. This would keep your code a bit cleaner if you want this functionality in multiple places and also allows you to easily extend the logging function further if desired.