BAD_ACCESS crash unless I create a string with # - Using ARC [closed] - objective-c

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
If I pass testString2 to the next view controller (where the string is assigned to a property of a NSManagedObject subclass), the app crashes soon after with a BAD_ACCESS error. I was able to determine the string was turning into a zombie a while after attaching it to the managed object and also assigning it to a class member of the receiving view controller, to try and eliminate this problem. However, it doesn't turn into a zombie until well after it has been assigned as described.
IF, however, I send testString instead of testString2 to the next view controller, no crashes and everything is happy. Incidentally, newKw is text from a text field, but using strings retrieved from a dictionary gives the same result. I have also tried using [NSString stringWithString:newKw] and other NSString methods in an attempt to create a brand new string, and I get the same result then too.
If I send nil instead of sending any string, there are no errors.
The managed object is never lost or corrupted if I pass testString. But if I pass testString2, it appears when the variable turns into a zombie, it takes out the managed object too because everything in the description is gone and is not shown as a fault. Following some other advice I have seen, I set a breakpoint for malloc_error_break, and in my log I see this:
Power Passage(3734,0x2dae1a8) malloc: *** error for object 0x9aa67b0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Here is where it is passed from the VC where the string originated:
if (proceed) {
NSString *testString = #"testing123";
NSString *testString2 = newKw;
[self.navigationController pushViewController:[[NewKeywordSummary alloc] initWithKeyword:testString2] animated:YES];
Here is the property where the pointer is getting stored in the managed object:
#property (nonatomic, retain) NSString * newKeyword;
Here is the method newKw is being sent to:
-(instancetype)initWithKeyword:(NSString *)kw
{
if (self = [self init]) {
//Create a new request
kwReq = [ppKeywordRequest keywordRequestInContext:editingContext];
newKw = kw;
kwReq.newKeyword = newKw;
}
return self;
}
Then the kwReq object is passed to the next VC:
-(void)viewBtnHandler:(UIButton *)btn
{
if (btn == addNewTagBtn) {
[self.navigationController pushViewController:[[TagSummaryVC alloc] initWithKeywordRequest:kwReq tagSubmission:nil] animated:YES];
}
}
and it goes here:
-(instancetype)initWithKeywordRequest:(ppKeywordRequest *)req tagSubmission:(ppTagSubmission *)t
{
if (self = [super init]) {
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
user = delegate.user;
phrases = [delegate.languagePhrases objectForKey:#"TagSummaryVC"];
if (req) {
editingContext = req.managedObjectContext;
kwReq = req;
}
else {
editingContext = [[NSManagedObjectContext alloc] init];
[editingContext setPersistentStoreCoordinator:[delegate.localDataContext persistentStoreCoordinator]];
delegate.editingContext = editingContext;
}
if (!t) {
ts = [ppTagSubmission tagSubmissionInContext:editingContext];
newTS = YES;
}
else
ts = (id)[editingContext objectWithID:[t objectID]];
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:#""
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
}
return self;
}
And it returns to NewKeywordSummary here:
if (alertView.tag == AttachAlert) {
if (index == 0)
return;
NSString *testString = kwReq.newKeyword;
NSLog([NSString stringWithFormat:#"Test variable is %#", kwReq.newKeyword]);
[kwReq addTagSubmissionsObject:ts];
[self.navigationController popViewControllerAnimated:YES];
}
}
My NSLogs before and after it is a zombie:
2014-08-04 21:00:52.306 Power Passage[3838:60b] Test variable is vcxvzcx
2014-08-04 21:00:53.378 Power Passage[3838:60b] Test variable is vcxvzcx
2014-08-04 21:00:55.908 Power Passage[3838:60b] Test variable is <__NSMallocBlock__: 0x8ea4b00>
(lldb)

Make sure your NSString references are assigned to a #property on your classes (plenty of examples of assigning and using properties on iOS) using an appropriate storage type (strong or retain, for example). And, yes you are correct any "standard" string in an iOS app is usually an instance of NSString, and thus required the "#" symbol in front.
Try setting a break point on Exceptions ... this will help you identify the offending statement.
I would suggest picking up a book on an intro to Objective-C / iOS / iPad development ... I think you'll find the information invaluable and a HUGE time saver.

I believe I have found the answer to my question. I ran into this post:
why can't I declare a variable like "newVariable" in Obj-C?
Quote from the link:"Objective-C has a naming convention for memory management that is enforced by the compiler. Methods that start with new (also "alloc", "copy", "mutableCopy") are required to return an object that will be "owned" by the caller. See the documentation." (thanks progrmr!)
and realized that I had a property that started with "new", and while it wasn't being synthesized it WAS generating setters/getters with #dynamic... so every time the member kwReq.newKeyword was referenced on my managed object, I am guessing that the compiler was trying to change ownership or otherwise alter the retain count - so after a couple times I was out of retains. So I changed the member name to reqKw, and I have no more problem. I was NOT getting any kind of warning on this.(thanks Apple:-)

Related

How can I silence Xcode anaylzer reporting "potential leak of an object stored into 'self'" for class method [duplicate]

This question already has an answer here:
Self managing/releasing objects reported as "Potential leak of object" under Xcode -> Product -> Analyse
(1 answer)
Closed 9 years ago.
I am almost certain that I don't have a leak in this code, yet the Xcode analyzer reports that there is a "potential" leak (Xcode 4.6.1).
+ (MySHA1hash *)sha1HashWithHashBytes:(unsigned char *)hash length:(unsigned int)length;
{
return [[[MySHA1hash alloc] initWithHashBytes:hash length:length] autorelease];
}
If the problem is that Xcode is reporting a false positive, I would like to figure out how to structure the code in a way to silence the warning.
It is also the possible that I am leaking in a way I don't understand, but If someone can see how I am actually leaking I would be glad to get that feedback as well.
This must have something to do with the init functions I call, because if I simply replace initWithHashBytes with init, then the leak is no longer reported. To that end I also include the body of initWithHashBytes.
- (id)initWithHashBytes:(unsigned char *)hash length:(unsigned int)length
{
if (hash != nil && length <= SHA_DIGEST_LENGTH) {
NSData *data = [NSData dataWithBytes:hash length:length];
self = [self initWithHash:data];
}
else {
self = nil;
}
return self;
}
- (id)initWithHash:(NSData *)hash
{
if ([hash length] <= SHA_DIGEST_LENGTH && (self = [super init]) != nil) {
finished = YES;
[hash getBytes:sha_Result];
hashValue = [NSNumber numberWithInt:[hash hash]];
}
else {
self = nil;
}
return self;
}
The line
self = nil;
in initWithHashBytes: (and initWithHash:) is the issue. You are allocating an object, but if you return nil from from initWithHashBytes:, that object will be leaked, because you'll call autorelease on nil rather than on the object you allocated.
Release self before you return nil and all should be good.
In this particular case there was obviously an error that needed to be fixed, but I have seen at times a need to suppress warnings that are completely understood to be non problems (i.e. a leak reported that is not actually a leak).
This is what I expected to need to do here, but it turned out that there was an actual leak. So I am glad it got fixed. I immediately found another problem that was a clear an unmistakable "false positive" (I know that the error is reported as a "potential leak" so in reality it isn't a false positive, but it doesn't mean I want to see it in the report every time I run the analyzer).
Because of this I still had the question of how to suppress these warnings. It turns out you can easily wrap code that you want the analyzer to bypass in a ifdef check for __clang_analyzer.
#ifndef __clang_analyzer__
... code you want to ignore ...
#endif
There was a very good write up on this here.
You are missing a [self release] before self = nil.
The object you get from alloc has a reference count of +1 which needs to be balanced by a call to release or autorelease. For the case where you return your actual object from sha1HashWithHashBytes:length: the autorelease in that class method takes care of everything.
For the case you return nil your init method is the last one that has a reference to that allocated object so it has to release it.

My NSString is determined to equal null, infuriating

This has been bugging me all night, It doesn't make any sense. This function returns whatever it's supposed to. EG, the issueName.
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
NSLog(#"The issue name = %#", issueName);
}
return self;
}
However if I try to access 'issueName' in the viewDidLoad: nothing, it's equal to null no matter what I do. I've tried cleaning, setting a custom setter, switching between a property or a Ivar... ect. What's so infuriating is that this string just disappears at this point in the programe.
What the hell is going on, this is infuriating.
Edit
This the the entire code that is relevant. And how I started off.
Dot h file:
#interface BFPaidAreaViewController : UITabBarController <BFNewsTableViewControllerDelegate> {
NSString *issueName;
}
-(id)initWithIssue:(NSString *)string;
Dot m file:
-(id)initWithIssue:(NSString *)string {
self = [super init];
if (self) {
// PLPiper I had it that way before, because I was fiddling out of frustration
issueName = [[NSString alloc] initWithString:string];
NSLog(#"This is Called, the issue name is equal to = %#", issueName);
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSLog(#"The issue = %#", issueName);
}
I'm calling the view controller like so:
BFPaidAreaViewController *pavc = [[BFPaidAreaViewController alloc]initWithIssue:#"test"];
This will log:
This is Called, the issue name is equal to = test
The issue = (null)
New Edit
Found the problem. It's a UITableViewController. Strange, when I change it's class to a UIViewController it works. Is this a bug or just normal behaviour? But more pressing, how to I get round this limitation?
(Just to explain what I've done UI wise, the UITabBarController is in a modal View. This works fine with a UIViewController.)
God Awful Fix
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
NSLog(#"The issue name = %#", issueName);
}
[self viewDidLoad];
return self;
}
Makes me feel dirty. But it will have to do for now, I can continue. If anyone can think of a solution please tell. Sorry about my feistiness, it was incredibly frustrating listening to people say, 'what the hell is this?? what is issueName?? an ivar??' when it was really implicit in the question.
Okay, first of all, replace:
self = [super initWithNibName:nil bundle:nil]; // Unneeded nil arguments
with:
self = [super init]; // Equivalent method, less processing involved.
Secondly, replace:
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
with just:
_issueName = [[NSString alloc] initWithString:string];
If issueName is a property (and you haven't #sythesized it to anything else) its representation should be _issueName.
The above fixes are more or less just make the code more succinct. The issue is probably with the code in viewDidLoad: (See below).
Now you can initialise your Issue object, and use the following code to display the issue name:
// Init:
Issue *myIssue = [[Issue alloc] initWithIssue:#"Example Issue"];
// Log:
NSLog(#"%#", myIssue.issueName);
And the log should show:
Example Issue
can you try this:
make the issueName a property, like
#property (strong, nonatomic) NSString *issueName;
then use it like this,
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSLog(#"The string = %#", string);
self.issueName = string;
NSLog(#"The issue name = %#", issueName);
}
return self;
}
if you are using the automated synthetized property (i.e not declaring the #synthentize manually for the issueName), then your iVar will be called _issueName instead of issueName
what do you get from the above code ?
I find this somewhat curious. You call [super initWithNibName:nil bundle:nil]. This leads me to believe that this might be a subclass of NSViewController. If you init an NSViewController like this, barring some other, pretty non-standard stuff, -viewDidLoad probably won't get called because there's no NIB to be loaded (because you passed nil to super). But clearly you're setting a breakpoint in -viewDidLoad so it's getting called (on something). This makes me think that you have this class specified in a XIB somewhere as a File's Owner or as a NIB-loaded custom object. If that's the case, it leads me to believe that the instance you're init-ing and the instance on which -viewDidLoad is being called aren't the same instance. You can confirm this for yourself by putting NSLog(#"self: %p", self); in each method and seeing whether they are the same or different.
If the instance that is getting a call to -viewDidLoad is NIB-loaded, then your init method won't be called. Instead it will use -initWithCoder
If you can elaborate on the situation here (i.e. how this is getting instantiated, are there any XIBs involved, etc), I will edit my answer to provide more help, but I don't think there's enough information here to be truly helpful.
I feel your frustration. Assuming standard behavior, any of the suggestions here should have worked. This only reinforces my suspicion that these are not the same instance (between -initWithIssue and -viewDidLoad.

How do I avoid a zombies error with xcode 4.5 w/o ARC?

When I run my application, which does not use ARC, in the xcode 4.5.1 (LLDB) debugger with zombies enabled, I get this error twice (2) when calling -[super dealloc] (-[NSObject dealloc]):
* -[V2APIClient class]: message sent to deallocated instance 0x9d865c0
* -[V2APIClient class]: message sent to deallocated instance 0x9d865c0
When I run the same application in the xcode 4.4.1 (LLDB) debugger, I get the error message once (1).
When I run a slightly earlier version of the same application in XCode 4.3.2, I don't get the error message at all (0). I will retry this with the same/newest code.
FYI - This appears to be exactly the same problem as this other post, which has not yet been answered:
-[Foo class]: message sent to deallocated instance on [super dealloc])
I attempted to avoid reposting the same question twice, but I was advised to proceed:
https://meta.stackexchange.com/questions/152226/avoiding-asking-a-question-thats-already-been-asked
Also, I also just asked the equivalent question in the Apple Developer Forums:
https://devforums.apple.com/thread/171282
Finally, here is the essence of my class:
#interface ASIHTTPRequestHandler : NSObject {
id _error;
}
#property (nonatomic,retain) id error;
#end
#implementation ASIHTTPRequestHandler
#synthesize error = _error;
-(id)init
{
self = [super init];
if (self)
{
self.error = nil;
}
return self;
}
-(void)dealloc
{
self.error = nil;
[super dealloc];// this is the line that appears to cause the problems
}
#end
Please help me resolve this problem.
I don't believe I am violating any memory management rules, but this error seems to imply otherwise. I'm hesitant to check in any new code until I can resolve this problem.
Thanks,
Chuck
p.s. For the record, here is the calling code:
PanoTourMgrAppDelegate *ptmAppDlgt = [PanoTourMgrAppDelegate getApplicationDelegate];
Settings *settings = ptmAppDlgt.settings;
Identification *identification = ptmAppDlgt.identification;
V2APIClient *v2ApiClient = [[V2APIClient alloc] initWithSettings:settings identification:identification];
NSDictionary *result = [v2ApiClient get_application_status];
BOOL success = [v2ApiClient callWasSuccessful:result];
if (!success)
{
id error = v2ApiClient.error;
NSString *mbTitle = nil;
NSString *mbMessage=nil;
if ([error isKindOfClass:[NSString class]])
{
mbTitle = #"Application version no longer suppported";
mbMessage = (NSString*)error;
[MessageBox showWithTitle:mbTitle message:mbMessage];
}
}
[v2ApiClient release]; // This is the line that indirectly causes the messages above
If you are messaging a deallocated instance, it is because you haven't managed memory correctly. You have release that is not balanced by a retain; an over-release.
First, do a "build and analyze" on your code. Fix any problems identified.
Next, run under Instruments with zombie detection enabled and turn on the reference count tracking feature. Then, when it crashes, inspect all the retain/release events to the object in question. You'll find an extra release. The challenge is to stick a retain into the right spot to balance the release.
(And as Rob rightly points out, it may simply be a case of an extra call to release.)
This is a bug in the debugger in some versions of Xcode.
I've just had it in Xcode 4.4.1 but it is not in Xcode 4.6. To exercise the bug I created a new "Single View Application" project, switched on "Enable Zombie Objects" in the scheme dialog, and ran the following code in the View Controller, with a break point set on the final line.
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *object = [[NSObject alloc] init];
[object release];
}
This results in the following message in the debugger:
-[NSObject class]: message sent to deallocated instance 0x6a7e330
You can also see the object in the variables section of the debugger now describes its class as _NSZombie_. If you remove the breakpoint the message is no longer shown. The call to the class method is made by the debugger after the object is correctly deallocated.

CFDictionary objectForKey message sent to deallocated instance

Does anyone know why this could be happening?
The error clearly says that it's trying to access objectForKey of a dictionary that has been deallocated. However in the stack trace I don't see any dictionary. How is this possible?
So here's the code:
-(CGSize)sizeConstrainedToSize:(CGSize)maxSize fitRange:(NSRange*)fitRange {
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self);
CFRange fitCFRange = CFRangeMake(0,0);
CGSize sz = CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0,0),NULL,maxSize,&fitCFRange);
if (framesetter) CFRelease(framesetter);
if (fitRange) *fitRange = NSMakeRange(fitCFRange.location, fitCFRange.length);
return CGSizeMake( floorf(sz.width+1) , floorf(sz.height+1) ); // take 1pt of margin for security
}
UPDATE:
Turns out this is a problem on NSCache:
#implementation NSCache (SharedCache)
+(NSCache*)sharedCache;
{
static NSCache* sharedCache = nil;
if (sharedCache == nil) {
sharedCache = [[[self alloc] init] retain];
[sharedCache setCountLimit:0];
}
return sharedCache;
}
if I removed the retain there it crashes..
it basically crashes here:
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self);
with that exception.
One of the properties or member variables is or contains a dictionary which has been deallocated. You might have code that is setting an attributes dictionary on the attributed string which is being over released.
Ultimately, the code you are highlighting is identifying an over released object but is not the crashing code. You need to find where the object is being over released. I recommend using Xcode performance tools, specifically the Zombies tool, to identify this crash. It's simulator only though so this will need to be reproduceable in the simulator to use the zombies tool.

NSMutableDictionary Singleton issue

I am coding Objective-C using the Cocos2D framework, and I have a singleton used for multiple purposes. One new purposes is to get and set character's "states" which are strings. I've recently made an NSDictionary for this purpose, but I have issues with the program freezing up when a method inside the singleton is called.
Here's the singleton code. I'm just leaving in the character state stuff:
.h
#interface ExGlobal : NSObject {
NSArray *charStates_keys;
NSArray *charStates_objects;
NSMutableDictionary *charStates;
}
#property(nonatomic, retain) NSMutableDictionary *charStates;
+(ExGlobal*)sharedSingleton;
- (NSString *)charState:(NSString *)charName;
- (void)set_charState:(NSString *)value forCharName:(NSString *)charName;
#end
.m
#import "ExGlobal.h"
#implementation ExGlobal
#synthesize charStates;
static ExGlobal* _sharedSingleton = nil;
+(ExGlobal*)sharedSingleton {
#synchronized([ExGlobal class]) {
if (!_sharedSingleton) {
[[self alloc] init];
}
return _sharedSingleton;
}
return nil;
}
+(id)alloc {
#synchronized([ExGlobal class]) {
NSAssert(_sharedSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedSingleton = [super alloc];
return _sharedSingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
exitName = #"ruinsSkyMid";
sceneChangeKind = #"reborn";
charStates = [[NSMutableDictionary alloc] init];
charStates_keys = [NSArray arrayWithObjects:#"Feathers", #"Hummus", nil];
charStates_objects = [NSArray arrayWithObjects:#"at wall", #"with Feathers", nil];
charStates = [NSMutableDictionary dictionaryWithObjects:charStates_objects forKeys:charStates_keys];
}
return self;
}
- (NSString *)charState:(NSString *)charName{
NSString *value = [charStates objectForKey:charName];
return value;
}
- (void)set_charState:(NSString *)charState forCharName:(NSString *)charName{
[charStates setObject:charState forKey:charName];
}
- (void)dealloc {
//I know it doesn't get called, but just in case
[charStates release];
[super dealloc];
}
#end
It's unclear to me what exactly the issue is when it freezes. When this happens, all I get in the console is:
Program received signal: “EXC_BAD_ACCESS”.
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
Previous frame inner to this frame (gdb could not unwind past this frame)
Previous frame inner to this frame (gdb could not unwind past this frame)
Which I'm sure doesn't help finding the issue. I found if I redefine charStates_keys, charStates_objects and charStates inside both the charState and set_charState methods, it seems to work without freezing, except set_charState does not change the state.
It isn't freezing, it is crashing. Hence the EXC_BAD_ACCESS. It looks like your Xcode installation is borked, too, as the two messages following should not happen.
Note that methods should not have _s in the name; not a cause of the problem, but a comment on following convention.
You aren't retaining charStates and that is likely the cause of the crash.
Not an answer as such but I didn't have enough space in the comments field above to post this, but it might be useful.
As bbum already said, your lack of retaining charStates is likely the problem.
If you are confused about when to retain and not retain objects there's a really good book called "Learn Objective-C on the Mac" and I know it's a Mac book but most of it applies to iPhone too. On page 171 of chapter 9 (Memory Management) it talks about the "Memory Management Rules" and how if you are confused about when to retain or not then you don't understand the simple rules of Objective C memory management.
Essentially if you create an object using new, alloc or copy, then the retain count is automatically set to 1 so the object is retained and does not require you to retain it and will require a subsequent release to deallocate.
If you create the object any other way then the object will be an autoreleased object.
Obviously these rules only apply within the standard iOS libraries and can't necessarily be applied to third party libraries.
I recommend anyone who doesn't fully understand memory management in Objective C read this book. I found highly enlightening even for my iPhone work.
Hope that helps/.