I am trying to fix a bug in an iPhone app, and find myself in front of this situation.
The instance variable myView is of type MYTextView, and MYTextView is a subclass of UITextView.
At the debugger I enter a few times in a row: p myView.text
the strange thing, as one can see below, is that I get different results:
(lldb) p myView.text
(NSString *) $34 = 0x095da870 #"พี(the-date-in-Thai-Language)
05"
(lldb) p myView.text
(NSString *) $35 = 0x0a2c5620 #"พี(the-date-in-Thai-Language)
"
(lldb) p myView.text
(NSString *) $36 = 0x09515f60 #"พี(the-date-in-Thai-Language)
щ"
(lldb) p myView.text
(NSString *) $37 = 0x09515f80 #"พี(the-date-in-Thai-Language)
㏀ड़ख़筀ज़툠ज़梐
⨈Ⴊ"
(lldb) p myView.text
(NSString *) $38 = 0x0a2c3800 #"พี(the-date-in-Thai-Language)
妜샷फ़"
(lldb) p myView.text
(NSString *) $39 = 0x095e7010 #"พี(the-date-in-Thai-Language)
"
Above the results $35 and $39 contain what I expect, the other lines contain garbage at the end of the expect string.
I don't understand how this changing garbage can be here, when I am supposed to be inside the debugger an pausing.
One more thing is that I am having this kind of problem only with the Thai language.
Does anyone have an idea of what could be going on?
This looks like a bug in the data formatter for NSString.
Are you on Xcode 4.6? If so I would love it if you filed a bug against Xcode with as much context as possible.
My guess is that there is some buffer that is not being managed properly.
When you use the p command, there are several different things lldb can do.
For simple concrete types (int, int *) it may only print the value in the variable (10, 0xffffffff83021600). It may have a formatter preference set (e.g. display the int in hexadecimal). It may have a more sophisticated python formatter (which can read different parts of memory and present a high-level view of the object to you). Finally, it may actually run code in your program to come up with a high level view of that variable.
In this case, with Xcode 4.6, this looks like a simple NSString ivar and so the expectation is that lldb can use its built-in python formatter for NSString objects. This built-in formatter looks at the object memory and knows how to reconstruct the actual string for display without running any code in your program. In which case, the content of the string should not be modified.
Earlier versions (and gdb) may run code in your program to format the string, and it is possible that the program could change a little in the process. There is a lot of work done to ensure that doesn't happen, but the possibility exists.
Enrico is suspecting that the built-in python formatter function for NSString may be misbehaving somehow in your program and providing string summaries that change as you re-call the formatter. Please do file a bug report at http://bugreport.apple.com/ with the details for how to reproduce this behavior.
Related
I have a variable in Swift code that runs in iOS simulator and contains an existing fileURL. I want to have the file opened in macOS (not the iOS Simulator) when I hit a breakpoint.
I added an action "Shell Command" to the breakpoint to open the file. The file exists because if I copy-paste the file's path to Terminal, it opens in Preview.
However, the Xcode console says the contrary:
The file /"/Users/tomkraina/Library/Developer/CoreSimulator/Devices/FBA16E00-9450-40E8-9650-1489A67E344C/data/Containers/Data/Application/BB97DB72-FF2A-4087-BD42-2934C63D3323/tmp/7E2303B8-0629-475A-862A-2550351FB448/OutlineExport.pdf" does not exist.
First Question: How do I tell Xcode to open a file with provided fileURL in a variable on breakpoint?
Next thing I tried was to open the file using LLDB, but I cannot find out how to evaluate a command parameter in LLDB, because backticks is only for scalars:
(lldb) shell open `temporaryFile.fileURL.path`
The file /105553157711856 does not exist.
Second Question: How to I evaluate argument parameter to get a string in LLDB?
I don't have a good answer for the first question. It would be interesting to check whether the path that is in the Xcode error message is correct - maybe it's getting it from the value incorrectly. If you copy the path from the error message, go to Terminal and try to open it, does that work? Anyway, this sounds to me like a bug in Xcode. It got some kind of path out of your variable and tried to open it, which should have worked. If you want to follow up, it's probably best to file a bug report with the Apple Feedback.
For the second question, you have to know a little about how variables work in lldb. Some variables have obvious values, for instance, in C a pointer has the pointer value, an integer the integer value, etc. Other variables (any kind of Struct being the obvious example) are actually containers of other values and don't really have a "value" themselves.
lldb can show you what a swift string really is using the --raw option:
(lldb) v --raw str1
(Swift.String) str1 = {
_guts = {
_object = {
_countAndFlagsBits = {
_value = -3458764513820540912
}
_object = 0x8000000100003f50 (0x0000000100003f50) strings`symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () + 4
}
}
}
That's probably really interesting to people working on the Swift Standard Library and has the virtue of being the truth. But for most purposes, it's not a terribly useful representation.
lldb handles that problem by adding a notion of "Summary Formatters" that generate a string representation for objects based on their type. There's one for "Swift.string" that digs around in the object, finds where the actual string is, and returns that text. If you don't pass --raw and there's a summary formatter, then lldb will show you the summary:
(lldb) v str1
(String) str1 = "some string here"
That is also the value that you want to try and open.
The backtick syntax in lldb gets the value of the entity, not its summary, which is why that didn't work for a swift string. However, you can fetch and act on the summaries for local variables using lldb's Python interpreter and the SB API. So for instance:
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> var = lldb.frame.FindVariable("str1")
>>> var.GetSummary()
'"some string here"'
So then if that was a file path you wanted to open, you can use Python to do that, like:
>>> os.system("open {0}".format(var.GetSummary()))
The file /private/tmp/some string here does not exist.
256
except of course your var has to hold the path to a real file...
If you want to learn more about the lldb Python API's the API docs are here:
https://lldb.llvm.org/python_api.html
and a general tutorial for using Python in lldb is here:
https://lldb.llvm.org/use/python-reference.html
And more information on variable formatting is here:
https://lldb.llvm.org/use/variable.html
It appears that XCtest "self measureBlock" is limited to milliseconds and 10 runs of the test code. Are there any ways to modify the behavior of measureBlock for more runs and/or nano or microsecond accuracy?
TL;DR
Apple provides a way to modify the behavior of measureBlock: by providing extra string constants but they don't support any string constants other than the default.
Long explanation
measureBlock calls the following function
- (void)measureMetrics:(NSArray *)metrics automaticallyStartMeasuring:(BOOL)automaticallyStartMeasuring withBlock:(void (^)(void))block;
//The implementation looks something like this (I can't say 100% but i'm pretty sure):
- (void)measureBlock:(void (^)(void))block {
NSArray<NSString *> *metrics = [[self class] defaultPerformanceMetrics];
[self measureMetrics:metrics automaticallyStartMeasure:YES withBlock:block];
}
defaultPerformanceMetrics is a class function that returns an array of strings.
From the Xcode source
"Subclasses can override this to change the behavior of
-measureBlock:"
Lovely, that sounds promising; we have customization behavior right? Well, they don't give you any strings to return. The default is XCTPerformanceMetric_WallClockTime ("com.apple.XCTPerformanceMetric_WallClockTime")
It turns out there aren't any string constants to return besides that one.
See the slides for WWDC 2014 Session 414 Testing in Xcode 6 (link).
I quote from page 158:
Currently supports one metric: XCTPerformanceMetric_WallClockTime
Nothing else has been added in Xcode 7 so it seems you're out of luck trying to modify measureBlock, sorry.
I've never found measureBlock: very useful. Check out Tidbits.xcodeproj/TidbitsTestBase/TBTestHelpers/comparePerformance https://github.com/tipbit/tidbits if you'd like to look at an alternative.
Let’s say I have the following code:
- (void) handlePanGesture: (UIPanGestureRecognizer*) sender
{
CGPoint foo = [sender translationInView:XXX];
}
Now when I place the cursor at the point marked as XXX and press Esc to get a list of completion suggestions, Xcode happily suggests all sensible symbols regardless of their type (including NSArray*, CGSize, or even SEL). Since Xcode presumably has enough information to know the argument type, why doesn’t it filter the suggestions down to only those compatible with UIView*?
I am wanting to capture a list of all running processes for OSX and save them as a file in Xcode / Cocoa. I googled this and all I found was:
[myWorkspace runningApplications];
And I am not sure how to do this. Please Help! Thank you!
As explained in QA1123, "process" means multiple different things on a Mac (and that changes over time).
A "process" at the level of Cocoa (or Carbon, or once upon a time Classic) is basically what an end-user thinks of as a process: an app, fba, launchitem, etc. A "process" at the level of BSD is what a Unix-trained sysadmin thinks of as a process: something that shows up in ps. A high-level process can have multiple BSD processes; the other way around used to be possible too (under Classic); you can also have BSD processes that have no high-level process; etc.
If you want the high-level definition, forget that QA, the method -[NSWorkspace runningApplications] that you mentioned returns exactly what you want: an array with an object for each such app, and those objects have all the info you want. How you save them in a file depends on what information you want about each one, what format you want to save that information in, etc. Here's a complete sample app that will save the URL of each app, one per line, to a file called "./appslist":
#include <Cocoa/Cocoa.h>
int main(int argc, char *argv[]) {
NSString *output = [NSString string];
for (NSRunningApplication *app in
[[NSWorkspace sharedWorkspace] runningApplications]) {
output = [output stringByAppendingFormat:#"%#\n",
[[app bundleURL] absoluteString]];
}
[output writeToFile:#"./appslist"
atomically:YES
encoding:NSUTF8StringEncoding
error:NULL];
return 0;
}
If you want the low-level definition, the code in that QA is still accurate. Or you could just exec (or system or NSTask) ps.
Anyway, here's a sample that (with the code from that QA) prints the pid of each running process to a local file called "./bsdlist":
int main(int argc, char *argv[]) {
kinfo_proc *procs;
size_t count;
int err = GetBSDProcessList(&procs, &count);
if (err) return err;
FILE *f = fopen("./bsdlist", "w");
for (size_t i=0; i!=count; ++i) {
fprintf(f, "%d\n", procs[i].kp_proc.p_pid);
}
fclose(f);
free(procs);
}
If you like the idea of scripting ps, as mentioned above, there are a number of ways to do this.
The DDTask library mentioned in Dominik's answer looks like the easiest way to do this. If you want to use NSTask directly, it takes a bit more code to set up an NSPipe for stdout, etc. There's a good general CocoaDev (it's scripting ls rather than ps, but the ideas are all the same.)
Or you could drop down to a lower level. You could explicitly fork/exec and pass the results through stdout, but the popen function is designed to wrap all of that up, and it's a lot easier to use. The GNU C Programming Tutorial shows how to popen ps -A and pipe it to grep init; the first half of this is all you need.
Whichever way you go, the problem is that you're going to get back a mess of strings you have to parse. The best thing to do is pass different flags to ps to only get what you actually want. If all you want is the command lines, ps -ax -ocommand= will give you nothing but that, one command line per line—no header line to skip, no columns to parse apart, etc.
If you're worried about efficiency: The QA says "exec'ing ps will require parsing the tool's output and will not use system resources as efficiently as Listing 1." And this is true; formatting the sysctl output into strings just to pass them over a pipe and parse them again is extra work that has to take some CPU time. But unless you're doing this millions of times, I doubt it uses enough to make a difference. The best approach (to this, and most cases) is to write the simpler code first and test whether it's fast enough; if it is, you're done.
yip to get high-level query the workspace as shown by abarnet
to get 'all' processes go lower-level and execute ps via an NSTask and read the response via a NSPipe.
there's sample code by apple ..
or use DDTask (a wrapper I wrote) github repository
NSString *exe = #"/bin/ps";
NSArray *args = #[#"-ax"];
NSString *res = [DDTask runTaskWithToolPath:exe andArguments:args andErrorHandler:nil];
[res writeToFile:#"./appslist"
atomically:YES
encoding:NSUTF8StringEncoding
error:NULL];
I wonder if Objective-C/Foundation has any special commands for reading user input from the console. Since it has NSLog for output maybe there is something else I could use instead of the scanf command.
I need to read some numbers (user input) into my tool. What is the best way to get these input in types like double or int? And how do I get user input into an NSString?
I was bored earlier and came across this issue of 'use scanf'. since I wanted to see if I could do it without dropping into c, the following came up:
NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
while (1)
{
NSData* data = [input availableData];
if(data != nil)
{
NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
}
I'm sure somebody could optimize this and make it nicer (this was used for a really simple PoC CLI tool)
The only real Cocoa support for input is NSFileHandle's fileHandleWithStandardInput. It isn't really more useful than scanf() if you ask me. But for getting input into specific types, well, that's pretty much NSFormatter's thing. There are already a lot of predefined formatter types for standard things, and you can make a custom formatter if you have more specialized needs. So if you need something a little more than scanf(), just read in the data (either as bytes with scanf() or data with NSFileHandle) and make an NSString from it and you can format it to your heart's content.
Nothing like scanf (which is a good thing). You can slurp data from stdin using NSFileHandle; for interactive input, fgets is better. You'll then want to use either strtol/strtoul/strtod, NSScanner, or NSNumberFormatter to convert the input to numeric types.