This should be so simple, and yet, it is evading me: When stopped at a breakpoint in the Xcode/gdb debugger, I wish to find the current refcount of an object derived from NSObject. How can I do this? I can't seem to find a way, and it is driving me mad.
Even better: go to the console (command+shift+r) and type:
p (int)[objectName retainCount]
And press enter. You can execute any statement you want on that console (it's GDB, at least for now), making whatever code calls you want, and it'll still try — and usually succeed — to come up with a result. 'p' is to print the result as a C primitive, 'po' would print the result as an Objective-C class (ie, by calling 'description' just as if you'd NSLog'd the instance). The 'int' is there because GDB can't always determine the return types at runtime, in which case you need to tell it.
It's as simple as
NSLog(#"retainCount:%d", [objectName retainCount]);
where objectName is whatever you named the object in question.
Please note that this does not work on NSStrings since they are managed differently.
For more info:
http://www.karlkraft.com/index.php/2009/04/22/dont-use-non-mutable-objects-to-understand-leak-detection/
Related
I made a breakpoint in Xcode with the jump commend to force passing some condition, but when it execute to line 168 it crash with message
"Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)"
why did that happen?
the console logged:
warning: MoreMultitypeCollectionViewCell.swift:178 appears multiple times in this function, selecting the first location:
MoreMultitypeCollectionViewCell.(updateButtonStateCkeck in _9A12557DCAB30EEB52DC7C2EA09487CD)() -> () + 1580 at MoreMultitypeCollectionViewCell.swift:178
MoreMultitypeCollectionViewCell.(updateButtonStateCkeck in _9A12557DCAB30EEB52DC7C2EA09487CD)() -> () + 1600 at MoreMultitypeCollectionViewCell.swift:178
my questions are:
How should I type in lldb to select location?
Is there a better way to force passing into If Statement without change code and rebuild project?
sometimes when I type 'po' in lldb or click print description in variable view, it will show fail message, how is that?
1) In lldb, the equivalent command is thread jump and you can specify an address as well as a line number there.
2) thread jump or the Xcode equivalent is an inherently dangerous operation. If you jump over the initialization of some variable, you will be dealing with bad data now and will likely crash. That sort of thing you can sometimes spot by eye - though Swift is lazy about initialization so the actual initialization of a variable may not happen where you think it does in the source. There are more subtle problems as well. For instance, if you jump over some code that as a byproduct of its operation retains or releases an object, the object will end up under or over retained. The former will cause crashes, the latter memory leaks. These retains & releases are generated by the compiler, so you can't see them in your source code, though you could if you look at the disassembly of the code you are jumping over.
Without looking at the code in question, I can't tell why this particular jump caused a crash.
But you can't 100% safely skip some of the code the compiler choose to emit. Looking at the disassembly you might be able to spot either (a) a better place to stop before the jump - i.e. stop past some retain or release that is causing a problem or jump to an address in the middle of a line so you still call a retain that's needed. You'll have to figure this out by hand.
3) There's not enough info to answer this question.
BTW, your image links don't seem to resolve.
I have an app submitted to Apple Store, and it passed. But I found there was a bug will make the app crash. Finally, I found the real problem is a snap of code has the opposite result.
code:
CGPoint now;
CGPoint old;
UIView *lastDot;
now = dotView.center;
if(lastDot){
//release version, will go here. then crashed.
old = lastDot.center;
}else{
//debug version, will go here. run ok.
}
I declare a UIView pointer, and check it with "if statement",
UIView *lastDot;
the debug version consider the lastDot as nil, but the release version think is not null.
can anyone give me any idea? thank you.
Depending on your compiler settings, debug builds usually initialize pointers to some sentinel values like 0xcccccccc.
UIView *lastDot = nil;
Should work as expected.
The issue is due to uninitialized pointer.
Initialize it to nil for avoiding such troubles.
UIView *lastDot = nil;
I know you already got the answer.
I just want to add some points too:
Pointers are just like any other variable: if you don't explicitly set
them to a value, the value will be undefined means a random value. If
you then accidentally access the variable, very bad things can happen.
Sometimes people recommend that pointers be assigned to NULL, the
universal value meaning "this pointer doesn't point at anything",
because a lot of code already checks for NULL pointers. For example,
if you call [nil release], it's guaranteed to do nothing. If you
passed an uninitialized pointer in who knows what would happen.
It's really just a safeguard against programmer error. If you
initialize pointers to a reasonable value, it's less likely that an
uninitialized pointer will cause a system crash sometime in the
future.
When you don't initialize a pointer and then try to use it, you have 3
problems:
It might be pointing at memory you don't have access to, in which case it causes a segmentation fault and crashes your program
It might be pointing at real data, and if you don't know what it's pointing to, you're causing unpredictable (and very hard to debug)
changes to your data.
You have no way of knowing if it's been initialized or not - because how do you tell the difference between a valid address and the address
that happened to be there when you declared the pointer?
Initializing every pointer to nil seriously decreases or eliminates
those problems:
If I try and use it, it will still segfault, but at least I can test if it's NULL and act accordingly - I can know that it WILL segfault,
and do something else. If it's a random value, I don't know anything
until it crashes.
If you initialize it to nil, I can't make it point to data unless I explicitly tell it to. So I only modify what I meant to.
As implied above, I can tell when I've initialized it and when I haven't, and make a decision.
Obviously it's a matter of style, and it is possible to write an
application where variables are only initialized to their intended
value, but I feel it is safer and easier to initialize them to nil
always. Even the best programmers make typos - nil makes it easier
to know when that's happened.
Reference :
eskimo
Initializing pointers always necessary
Is this:
[self showInWindow:window];
what get called after delay by this code:
[self performSelector:#selector(showInWindow:)
withObject:window
afterDelay:delay];
or am I misunderstanding the method?
Edit: the problem I'm having is that the method showInWindow get called after the delay but behaves like [self showInWindow:nil]. Any suggestion?
Yes, that's what gets called. (After the delay, of course.)
The documentation doesn't really explain what it means to "perform the selector", but what it means is exactly what you suspect.
There is one small difference between using performSelector:withObject: type methods and sending the message directly: they only work if the object is actually an object (that is, an id, a pointer to an Objective C object). But window obviously is an object.
(Strictly speaking, this isn't quite true. If you pass something that's the same size as an id or smaller, it will often work. In some cases it won't. In some cases it will work, but is illegal. In some cases, it will work and is legal but Apple strongly recommends against it. There are no cases where it's a good idea—so instead of learning the specific rules, just assume it never works. The only reason to bring this up is that this used to be common practice in Objective C back in the NeXT days, so you may occasionally still see it in other people's today.)
For more information about the performSelector: family, see the NSObject Protocol Reference, and the SO question Using -performSelector: vs. just calling the method. (For information specifically about the afterDelay: variants, see the documentation linked above.)
From the later edit to the question:
the problem I'm having is that the method showInWindow get called after the delay but behaves like [self showInWindow:nil]. Any suggestion?
First, in what way does it "behave like" the parameter is nil? Is the parameter actually nil? (Just log it in the showInWindow: implementation; if you haven't overridden the base implementation, just add an override that logs and calls the base.)
Second, if it actually is nil, was it nil at the time you sent performSelector:withObject:afterDelay:? If so, obviously it'll still be nil when the selector is sent. Also, make sure window really is an id rather than some other type. (Note that if you've got members, properties, globals, and/or locals sharing the name window, it can be confusing which one you're referring to. This is a common source of problems.)
If it's actually not nil when you schedule it, but is nil when it arrives, there are a few ways that could happen, but they're all less likely, and trickier to debug, than these two cases, so let's rule them out first.
Yes, that's what it does... Although keep in mind that it may take longer than the delay to execute. This method basically sets up an NSTimer in the current thread's run loop, so if your thread gets busy doing heavy duty work and the run loop takes longer than your delay to come back, your method will get executed later.
I've read the question below, and the story SEEMS simple:
What exactly is super in Objective-C?
Yet ...
- (id) init
{
NSLog(#"self=%p, super=%p", self, super);
}
That prints out "self=0xa83dc50, super=0xbfffe8d0". The addresses are NOT THE SAME???!?!?
That second address seems like a "special value" or something. What does it mean?
Thanks to bbum for pointing out that this value is the stack address of a special struct used by the compiler to implement the "super" behavior.
I can call [super init] and the call seems to work, or at least, nothing explodes ... not immediately. Calling [((id)0xbfffe8d0) init] fails hard with EXC_BAD_ACCESS.
Then there is the REALLY WEIRD part.....
I've got a piece of code that for no explainable reason throws a "NSGenericException: collection was mutated while being enumerated" exception. Inside a DIFFERENT object (basically a wrapper that has a pointer to the NSEnumerator), commenting out the call to "[super init]" causes the exception to not happen. If I could, I'd put out a $$$ reward for an answer to THAT mind-bender.
"id sups = (id)0xbfffe8d0" ... that also leads to "collection is modified." ... WTF? Ok, so I'm posting a 2nd question for that bizzariotity ...
I originally came here with one of those "bizarre symtoms" bugs, that turned out to be entirely unrelated (typical for such things): Does casting an address to (id) have side-effects??? Is Address 0xbfffe8d0 special? (fixed: issue was with _NSCallStackArray)
However, the content above the line is still valid, and the response still excellent. Read it if you want to understand ObjC just a little bit deeper.
You are messing with the man behind the curtain and he is punishing you for it... :)
super is a bit of compiler magic, really. When you say [super doSomething], the compiler will emit a call to objc_msgSendSuper() instead of objc_msgSend(). Most of the time -- there are some special cases.
In general, you should treat super as only a target for method calls. It should never be stored anywhere and should never be considered anything but that target expression for messaging.
In fact, uses of super that involve storage should likely be flagged by the compiler.
In terms of your bug, that sounds an awful lot like there is either corruption of memory, an over-release, and/or concurrency going on. You'll need to provide more code related to the enumeration and other relevant code to deduce further.
I have a simple question about debugging on Xcode and GDB.
I often run into an error:
unrecognized selector sent to instance 0x1081ad0
which makes the program load into GDB. Is there an easy way to examine what instance is located in that memory from GDB?
po 0x1081ad0
po = Print Object.
You can even call methods, like
po [myArray objectAtIndex:0]
Note that it only works on objects, so
po 1
will crash your program.
Steven is correct — the gdb command po is a shortcut for print-object, which actually calls -debugDescription (not -description, as you might expect) on the object provided as an argument. In many cases you'll see the same result from both methods, since one calls the other unless overridden. (See the related Note: callout on this Apple technote for details. Note that in their code sample, po $r3 prints the contents of a PowerPC register, but you can use any object pointer/reference, including Intel registers, etc.)
Also, be aware that print-object will only work on valid objects that haven't been deallocated. It won't help at all if you're sending a message to a borked pointer. Given the error you cited, though, it would seem that it's a valid object instance, it just doesn't implement the method you're trying to invoke.
It's also remotely possible that the object has already been destroyed. This answer should help in that case.
Edit:
There are other ways to "examine" objects in the debugger. I asked this SO question about Xcode data formatters, which is one way you can determine how a custom class appears in the Summary column of the debugger. The documentation linked from that question explain how it works. I've found the summary approach to help a lot with seeing the state of an object.
There are a couple of things you can do.
You can insert a break point that will trigger every time you have an exception, so basically create a break point for this (go to breakpoints and create a new one): -[NSException raise]
Alternatively, you can actually see what the object at that mem location is:
info symbol 0x1081ad0 or
info line *0x1081ad0
There's more info at the cocoadev wiki entry for exceptionhandling and debugging tips for objective C at cocoawithlove.
Your instance is not valid. You have release the object somewhere else, but you did not clear out your pointer... enable Zombie detection.