drawInRect:withAttributes dies with "message sent to deallocated instance" - ios

I am updating an app for iOS 7. One of the changes is switching to the new drawInRect:withAttributes function instead of the deprecated drawInRect:withFont...
This was working fine on the iOS 7 beta, but today after upgrading to the latest iOS 7 version, the app crashes on the line:
[text drawInRect:theRect withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:fontSz], NSFontAttributeName, color, NSForegroundColorAttributeName, nil]];
With the message:
*** -[NSStringDrawingTextStorage textContainerForAttributedString:containerSize:lineFragmentPadding:]: message sent to deallocated instance 0x187ed0f0
I tried running the Zombie instrument, which is not helpful at all neither the allocation nor the release of the object in question are in my code. Specifically I get the message:
An Objective-C message was sent to a deallocated 'NSStringDrawingTextStorage' object (zombie) at address: 0x169edc50.
And the malloc/release of the object are under the caller:
[NSStringDrawingTextStorage stringDrawingTextStorage]
What am I doing wrong?

I was able to work around this by trimming the leading whitespace (including newline characters) from the NSString I was rendering. Here's my category method:
- (NSString*)stringByTrimmingLeadingWhitespace
{
NSUInteger index = 0;
while((index < [self length]) && [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember: [self characterAtIndex: index]])
{
index++;
}
return [self substringFromIndex: index];
}
Unfortunately if you must preserve the leading newline character(s), I do not have an alternative answer.

Related

Xcode 6.3 (and 6.2) hits breakpoint on [UIFont fontWithName: size:]

In My iOS Application im using a class (DKTheme) to keep my fonts and images in a centralised place. my implementation looks like this.
+ (instancetype)theme {
static DKTheme *_theme = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_theme = [[DKTheme alloc] init];
});
return _theme;
}
- (id)init {
self = [super init];
if (self) {
[self setupTheme];
}
return self;
}
- (void)setupTheme {
// some code here
self.smallButtonFont = [UIFont fontWithName:#"Helvetica-Bold" size:13.0f];
//some code here
}
And when i run this code in device (iPhone 5C, iOS8.3 and iOS8.2), xcode hits breakpoint on the line self.smallButtonFont = [UIFont fontWithName:#"Helvetica-Bold" size:13.0f]; if i click continue execution button, application continue running without crashing and my font property(self.smallButtonFont) is successfully initialised.
and i noticed one more thing, i have several [UIFont fontWithName: size:]; calls and breakpoint hits only first time call.(if i comment the first one then next method call hits the break point). it is really annoying this breakpoint issue, any help would be thankful.
You have added a breakpoint exception in Xcode and configured it to break on all exception types, C++ and Objective-C. The problem is that C++ code sometimes uses exceptions for non-exceptional situations. It may use it just as a form of flow control or returning "failure" from a function.
Unless you have a specific C++ exception that you need to debug because it's actually causing a problem, probably best to configure that breakpoint to just break on Objective-C exceptions and not C++ exceptions. The C++ exceptions can be safely ignored.
This always happens when you added All Exceptions breakpoint. Here, you need to add only objective-c breakpoint.
Follow these steps:
Select Breakpoints debugger, right click on "All Exceptions".
Now select click "Edit Breakpoint"
Change Exception type to "Objective-c Exception"
At first. Of course it is exception breakpoint. You can add or delete it in "Breakpoints tab". My screenshot can help you with it.
Reason of generation this exceptions:
You have some default fonts in iOS. If you try to generate fond with unavailable font name. You see exception like this. Maybe in your code you use some another font name. I mean not only #"Helvetica-Bold". And now you have a question: How can i now, Is font available in my OS. You can print all available fonts using this method:
- (void)fontsList
{
NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyNames]];
NSArray *fontNames;
NSInteger indFamily, indFont;
for (indFamily=0; indFamily<[familyNames count]; ++indFamily) {
NSLog(#"Family name: %#", [familyNames objectAtIndex:indFamily]);
fontNames = [[NSArray alloc] initWithArray:[UIFont fontNamesForFamilyName:[familyNames objectAtIndex:indFamily]]];
for (indFont=0; indFont<[fontNames count]; ++indFont) {
NSLog(#" Font name: %#", [fontNames objectAtIndex:indFont]);
}
}
}
Best regards. And please add some info if you still have this problem.
If it's not that you've set up a breakpoint - it could be related to calling methods within init.
unsafe calls to self
Also see my notes about font caching and loading.
I had this problem as well, and solved it without changing my breakpoint settings. In my case, the problem was that my app had a framework whose info.plist file listed a provided font that was also listed as a provided font by the app itself (in the Fonts provided by application). Removing the duplicate fixed this issue.

instancesRespondToSelector returning true when it shouldn't

I'm writing an iOS application that is to be backwards compatible up to iOS 6.0.
In iOS 7, the NSString instance method drawInRect:withAttributes: replaced drawInRect:withFont:lineBreakMode:alignment:. To determine which method to use, I have the following code:
if ([NSString instancesRespondToSelector:#selector(drawInRect:withAttributes:)]) {
NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
[textStyle setLineBreakMode:NSLineBreakByTruncatingTail];
[textStyle setAlignment:NSTextAlignmentLeft];
[[self title] drawInRect:_textRect withAttributes:#{NSFontAttributeName: [UIFont boldSystemFontOfSize:FONT_SIZE], NSParagraphStyleAttributeName: textStyle}];
}
else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[self title] drawInRect:_textRect withFont:[UIFont boldSystemFontOfSize:FONT_SIZE] lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentLeft];
#pragma clang diagnostic pop
}
Since drawInRect:withAttributes: was introduced in iOS 7, instancesRespondToSelector: should return false when running on an earlier version of iOS. However, when testing this on a device running iOS 6.1, it returns true, and subsequently crashes when attempting to call drawInRect:withAttributes:. Does anyone have any idea what's happening, or what I'm doing wrong?
Just because a method wasn't part of the public API for a given release, doesn't mean that it didn't exist. My guess is that -drawInRect:withAttributes: was private API in iOS 6, then was promoted to public in iOS 7. You can test for some other condition that you know is only true on iOS 6 for your conditional here. e.g. if (NSClassFromString(#"SomeiOS7OnlyClass") != Nil).

Apple rejected my app for crashing on iphone 5

I know this question has been asked several times, but Im looking for a more general answer.
I have developed an app for iOS 6, I tested it on simulator(Retina 3.5 and 4 inch) and also on an iPhone 4 device.
It has never crashed but when I submitted the app to Apple and they answered with:
We found that your app crashed upon launch on iPhone 5 running iOS 6.1.3,
Looking at the crash log
We see that it crashes in line 164 from a index out of bounds, which makes sense because I have this code there:
I added that "if" to stop the execution whenever the indexTimesArray was bigger than the length of the array and see why that happened, but I was unable to reproduce the error. I never get an index out of bounds as they do...
It's true that I haven't test it on a iPhone 5 device, but I have XCode 4.6 and iOS 6.1 on my computer, and also a iPhone 4 with iOS 6.1.3, but it's also true that the guys at Apple are getting the app crashed, so how to reproduce the error?
I tried to install the app from TestFlight because it installs it as a brand new app, just like they do when they test it, but still no errors...
How can I reproduce the error? Could it be a problem with th build settings?
Thanks
[EDIT]
I initialize the contents of timesArray in the init method of the object, like this:
- (id)init{
self = [super init];
df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"yyyy-MM-dd HH:mm"];
rangeDates = [[NSArray alloc]initWithObjects:#"2013-04-11 10:00", #"2013-04-12 10:00", #"2013-04-13 10:00", #"2013-04-14 10:00", nil];
timesArray = [[NSArray alloc]initWithArray:[NSArray arrayWithObjects:#"10:00", #"11:00", #"12:00", #"13:00", #"14:00", #"15:00", #"16:00", #"17:00", #"18:00", #"19:00", #"20:00", #"21:00", #"22:00", nil]];
colorDictio = [[NSDictionary alloc]initWithObjects:[NSArray arrayWithObjects:[UIColor colorWithRed:0.74 green:0.66 blue:0.37 alpha:1.0], [UIColor colorWithRed:0.64 green:0.15 blue:0.11 alpha:1.0], [UIColor colorWithRed:0.313 green:0.65 blue:0.69 alpha:1.0], [UIColor colorWithRed:0.79 green:0.4 blue:0.59 alpha:1.0], [UIColor colorWithRed:0.45 green:0.55 blue:0.53 alpha:1.0], [UIColor colorWithRed:0.14 green:0.27 blue:0.66 alpha:1.0], nil] forKeys:[NSArray arrayWithObjects:#"showers area", #"zinctalks", #"zincnetwork", #"zincshows", #"zinclabs", #"zinczone", nil] ];
return self;
}
To figure out how to reproduce that error you have to look at the code where you create timesArray.
The out of bounds error happens because [timesArray count] is less than 2 (or the whole array is nil). So you have to figure out which condition leads to an array with one or zero objects. Maybe it happens because there is no internet connection.
It's always a good idea to wrap objectAtIndex: in a check for the actual size of the array.
I would replace else { with else if ([timesArray count] >= 2) { and add an additional else that handles <2 arrays.
First of all this is not a OS related error. Your app is crashing because the wrong index of array is being accessed.
How can I reproduce the error? : Try to use the same credential which you must have provided to apple.
Could it be a problem with th build settings? : No.
To debug the error what you can do is try to print the value of indexTimesArray before the if. Also, try to print all the values you are passing to access the array element. Which will help you track the wrong index which is being sent.
Thanks to #mayur, his comment is the right answer, "I had faced a similar error earlier with arrays in Objective-C... My suggestion would be to use self with NSMutableArrays or NSArrays"

heightForRowAtIndexPath occasionally crashes when loading, NSCoding - iOS

Some information about the way I am saving my data: I have an array of View Controllers that are added and deleted by the user (this is basically a note taking app and the View Controllers are folders). The View Controllers have several dynamic properties that the app needs to save as well as the notes array within them, and then the Note objects themselves have a few properties that need to be saved. The View Controllers and the Notes both of course have the proper NSCoding stuff, this is the one on the View Controller for example:
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.folderName forKey:#"lvcTitle"];
[encoder encodeObject:[NSNumber numberWithInt:self.myPosition] forKey:#"myPosition"];
[encoder encodeObject:self.notes forKey:#"notes"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.folderName = [decoder decodeObjectForKey:#"lvcTitle"];
NSNumber *gottenPosition = [decoder decodeObjectForKey:#"myPosition"];
int gottenPositionInt = [gottenPosition intValue];
self.myPosition = gottenPositionInt;
self.notes = [decoder decodeObjectForKey:#"notes"];
return self; }
The array of Controllers belongs to a Singleton class. NSCoding is pretty confusing to me even though it's considered to be simple stuff, but so far I've had success with only telling the Singleton to save the Controllers array - which then (successfully) saves all of the contained properties of the View Controllers, their properties and all of the Notes' properties as well. Here is the code in the Singleton:
- (void) saveDataToDisk:(id)object key:(NSString *)key {
NSString *path = [self pathForDataFile];
NSMutableDictionary *rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:object forKey:key];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path]; }
- (void) loadDataFromDisk {
NSString *path = [self pathForDataFile];
NSDictionary *rootObject;
rootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if ([rootObject valueForKey:#"controllers"] != nil) {
self.controllers = [NSMutableArray arrayWithArray:[rootObject valueForKey:#"controllers"]];
firstRun = false;
LabeledViewController *lastOneThere = [self.controllers objectAtIndex:self.controllers.count-1];
lastOneThere.isFolderAddView = TRUE;
}else{
firstRun = true;
}
}
I then call the save method several times in the Folder View Controllers:
[singleton saveDataToDisk];
And this will work well several times, until I randomly get a crash right when the app is loading up. The culprit is heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
Note *currentNote = [self.notes objectAtIndex:indexPath.row];
if (currentNote.associatedCellIsSelected) {
return currentNote.myHeight + NOTE_BUTTON_VIEW_HEIGHT;
}
return NORMAL_CELL_FINISHING_HEIGHT; }
I get the following error:
2012-06-07 08:28:33.694 ViewTry[1415:207] -[__NSCFString associatedCellIsSelected]: unrecognized selector sent to instance 0x8904710
2012-06-07 08:28:33.696 ViewTry[1415:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString associatedCellIsSelected]: unrecognized selector sent to instance 0x8904710'
*** First throw call stack:
I understand that "__NSCFString" and "unrecognized selector sent to instance" means that there is a string somewhere there shouldn't be, as associatedCellIsSelected is a bool. However, if I only return "currentNote.myHeight" in heightForRow, I also get the same __NSCF error with myHeight, which is a float. If I take out heightForRow all together, everything works except for the appropriate height definitions.
BTW, the table view that heightForRowAtIndexPath is referencing is made in loadView AFTER the notes array is made and populated. I just don't understand why this error would only pop up every once in a while (like 5-10 opens, savings, closings and reopenings of app), seemingly random - I cannot find the pattern that causes this behavior. Any pointers?
Sorry for the mess, I'm new to iOS programming and I'm sure I'm doing a lot of things wrong here.
Edit - Also, once the app has crashed, it stays crashed every time I reopen it (unless I disable heightForRow) until I uninstall and reinstall it.
When you see an "unrecognized selector" error and the receiver type is not the kind of object that you coded (in this case __NSCFString instead of Note), the odds are that you have a problem where the object you intended to use has been prematurely released and its address space is being reused to allocate the new object.
The fix depends on tracking down where the extra release is happening (or retain is not happening). If you can show the #property declaration for notes it might shed more light on the situation.
One quick thing to do is choose Product->Analyze from the menu and fix anything it flags. It won't catch everything but it's a good sanity check to start.

iPhone SDK releasing NSString

I'm new to Objective C and I have a simple question about memory management.
This is a simple method for a Button which changes a UILabel with the text in a UITextField.
-(IBAction) setLabel
{
NSString *inputText = [[NSString alloc]initWithString:myTextField.text];
[myLabel setText:inputText];
[inputText release];
}
This code works fine. But If I change this code to following,
-(IBAction) setLabel
{
NSString *inputText = [[NSString alloc]initWithString:#"some string value"];
inputText = myTextField.text;
[myLabel setText:inputText];
[inputText release];
}
Then application crashes on runtime. I have to remove the line [inputText release]; to run application without crashing.
As far as I know if I created something with 'alloc' I have to release it. But here, if I release that string app crashes.Could someone please explain the reason?
Thanks in advance
The reason that the release is crashing is because you're reassigning inputText to myTextField.text. The call to release is now releasing that string instead of the one allocated on the first line of setLabel. If you use another variable for that assignment, it should fix the crash.
I know this is not a direct answer to your question but you should try to use the autorelease pool to not have to worry about these details. Thus, if you had written your code as follows:
-(IBAction) setLabel
{
NSString *inputText = [NSString stringWithString:myTextField.text];
[myLabel setText:inputText];
}
the code is more readable and, moreover, you are not responsible for releasing the inputText string instance.

Resources