Finding minor performance hogs in Objective-C - iphone

To discover perfomance hogs in a specific method I often do something like this:
// Some line of code
LogTimeInterval();
// Some other line of code
LogTimeInterval();
// Some other line of code
LogTimeInterval();
Where LogTimeInterval is defined as:
void LogTimeInterval()
{
static NSDate *_previousDate;
static NSInteger _counter = 0;
NSDate *date = [NSDate date];
if (!_previousDate) {
_previousDate = date;
}
NSLog(#"LINE %d: %f", _counter++, [_previousDate timeIntervalSinceDate:date]);
_previousDate = date;
}
This allows me to discover which lines of code are taking more time than necessary. However, it requires modifying the code and can be cumbersome when there is branching logic.
Is there a most efficient way to do this micro-level analysis for a specific method?

Try XCode's built-in Profiler. Among the tools it has, there is a time profiler. Check this link for a nice tutorial on how to use it

Related

how to determine how much time needed when calling moveItemAtURL:toURL: or replaceItemAtURL:WithItemAtURL:

When moving file from one place to another, or when replacing file, I always use the methods moveItemAtURL:toURL: or replaceItemAtURL:WithItemAtURL: from NSFileManager.
When calling these methods, I want to determine how much time needed, so that I can use the NSProgressIndicator to tell users how long it's going to take. Just like when you are moving file using OSX, it tells u how much time remaining.
I have looked at the apple doc but couldn't find any information regarding this.
Wondering if this can be implemented, please advise.
You can't know in advance haw long it going to take. What you can do is compute the "percent complete" while you are copying the file. But to do that you need to use lower level APIs. You can use NSFileManagers attributesOfItemAtPath:error to get the file size and NSStreams for doing the copying (there are so many way to do this). Percent complete is bytesWritten / totalBytesInFile.
--- Edit: added sample code as a category on NSURL with a callback block passing the total number of bytes written, percen complete and estimated time left in seconds.
#import <mach/mach_time.h>
#interface NSURL(CopyWithProgress)<NSObject>
- (void) copyFileURLToURL:(NSURL*)destURL withProgressBlock:(void(^)(double, double, double))block;
#end
#implementation NSURL(CopyWithProgress)
- (void) copyFileURLToURL:(NSURL*)destURL
withProgressBlock:(void(^)(double, double, double))block
{
///
// NOTE: error handling has been left out in favor of simplicity
// real production code should obviously handle errors.
NSUInteger fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil].fileSize;
NSInputStream *fileInput = [NSInputStream inputStreamWithURL:self];
NSOutputStream *copyOutput = [NSOutputStream outputStreamWithURL:destURL append:NO];
static size_t bufferSize = 4096;
uint8_t *buffer = malloc(bufferSize);
size_t bytesToWrite;
size_t bytesWritten;
size_t copySize = 0;
size_t counter = 0;
[fileInput open];
[copyOutput open];
uint64_t time0 = mach_absolute_time();
while (fileInput.hasBytesAvailable) {
do {
bytesToWrite = [fileInput read:buffer maxLength:bufferSize];
bytesWritten = [copyOutput write:buffer maxLength:bytesToWrite];
bytesToWrite -= bytesWritten;
copySize += bytesWritten;
if (bytesToWrite > 0)
memmove(buffer, buffer + bytesWritten, bytesToWrite);
}
while (bytesToWrite > 0);
if (block != nil && ++counter % 10 == 0) {
double percent = (double)copySize / fileSize;
uint64_t time1 = mach_absolute_time();
double elapsed = (double)(time1 - time0)/NSEC_PER_SEC;
double estTimeLeft = ((1 - percent) / percent) * elapsed;
block(copySize, percent, estTimeLeft);
}
}
if (block != nil)
block(copySize, 1, 0);
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSURL *fileURL = [NSURL URLWithString:#"file:///Users/eric/bin/data/english-words.txt"];
NSURL *destURL = [NSURL URLWithString:#"file:///Users/eric/Desktop/english-words.txt"];
[fileURL copyFileURLToURL:destURL withProgressBlock:^(double bytes, double pct, double estSecs) {
NSLog(#"Bytes=%f, Pct=%f, time left:%f s",bytes,pct,estSecs);
}];
}
return 0;
}
Sample Output:
Bytes=40960.000000, Pct=0.183890, time left:0.000753 s
Bytes=81920.000000, Pct=0.367780, time left:0.004336 s
Bytes=122880.000000, Pct=0.551670, time left:0.002672 s
Bytes=163840.000000, Pct=0.735560, time left:0.001396 s
Bytes=204800.000000, Pct=0.919449, time left:0.000391 s
Bytes=222742.000000, Pct=1.000000, time left:0.000000 s
I mostly concur with CRD. I just want to note that under certain common circumstances, both -moveItemAtURL:toURL: and -replaceItemAtURL:WithItemAtURL:... are very fast. When the source and destination are on the same volume, no data has to be copied or moved, only metadata. When the volume is local (as opposed to network-mounted), this typically takes negligible time. That said, it is appropriate to plan for the possibility that they could take significant time.
Also, he mentioned the copyfile() routine for moving files. A copy followed by deleting the original is the necessary approach when moving a file between volumes, but the rename() system call will perform a move within a volume without needing to copy anything. So, a reasonable approach would be to try rename() first and, if it fails with EXDEV, fall back to copyfile().
Finally, the exchangedata() system call can be used as part of a reimplementation of -replaceItemAtURL:WithItemAtURL:....
I don't recommend the approach suggested by aLevelOfIndirection because there are a lot of fiddly details about copying files. It's much better to rely on system libraries than trying to roll your own. His example completely ignores file metadata (file dates, extended attributes, etc.), for example.
The methods moveItemAtURL:toURL: and replaceItemAtURL:WithItemAtURL: are high-level operations. While they provide the semantics you want for the move/replace, as you've found out, they don't provide the kind of feedback you wish during those operations.
Apple is in the process of changing lower-level file handling routines, many are now marked as deprecated in 10.8, so you'll want to pick carefully what you choose to use. However at the lowest levels, system calls (manual section 2) and library functions (manual section 3), there are functions that you can use that are not being deprecated.
One option, there are others, is the function copyfile (manual section 3) which will copy a file or folder hierarchy and provides for a progress callback. That should give you most of the semantics of moveItemAtURL:toURL: along with progress, but you'll need to do more work for replaceItemAtURL:WithItemAtURL: to preserve safety (no data loss in case of error).
If that doesn't meet all your needs you can also look additionally at the low-evel stat and friends to find out file sizes etc.
HTH

how to define a returning NSString function in Objective-C / Xcode using a temporary variable?

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.

iOS Development: What's a simple way to calculate the number of seconds that have passed between two events?

I need to calculate the number of seconds that have passed between two events on the iPhone. To do so, I need to store the time that the first event occurred and check it against the time the second event occurred to see if more than 30 seconds has passed.
I'm about to begin trying to accomplish this using the NSDate class, but I was wondering if there's a simpler way to do this without using objects, as I would prefer to store simple, intrinsic values instead of objects.
Thanks for your wisdom!
If you really want to avoid storing objects, you can do something like:
double startTime = [[NSDate date] timeIntervalSince1970];
//Run your other code
double endTime = [[NSDate date] timeIntervalSince1970];
if (endTime - startTime > 30) {
//30 seconds have passed
}
You can use
[NSDate dateWithTimeIntervalSinceReferenceDate:anotherDate];
or use the initWithTimeIntervalSinceReferenceDate: method
have you seen UIEvent's timestamp?
example:
double event1Timestamp, event2Timestamp; ///< ivars in your class?
{ // some function/method body
event1Timestamp = event1.timestamp;
}
{ // some other function/method body
/*** later that day ***/
event2Timestamp = event2.timestamp;
if (30 <= (event2Timestamp-event1Timestamp)) {
printf("ok, it's been 30 seconds");
}
}
where event1 and event2 are naturally the incoming events

Comparing to NSDates not working, incompatible pointer types

Hey i am trying to compare the date on which the user opens the app to the date it is currently. (basically, how long they've had the app in days)
Here is the code:
- (NSInteger) daysAfterDate: (NSDate *) aDate
{
NSTimeInterval ti = [self timeIntervalSinceDate:aDate]; //#1
return (NSInteger) (ti / D_DAY); //#2
} //#3
-(void)load {
NSDate *birthdate = [prefs objectForKey:#"Birthdate"];
rock_Age = daysAfterDate(birthdate);
}
errors:
1.) it tells me incompatible types in initialization
2.) D_DAY Undecared
warning:
3.) control reaches end of non void function
If i did this completely wrong, (because for the life of me i cannot understand the NSDate class :/) I would gladly take an alternative to doing this :)
all help is appreciated,
Thank you
-Jackson Smith
The load method is correct, the following should work for -[daysAfterDate:].
#define D_DAY 86400
-(NSInteger)daysAfterDate:(NSDate *)aDate {
NSTimeInterval ti = [[NSDate date] timeIntervalSinceDate:aDate];
return (NSInteger) (ti / D_DAY);
}
1) is because self is probably no NSDate. Use [NSDate date] to get the current time/date.
2) is because you have to define D_DAY
3) only happens because of 2).
I hope this helps.
The following article might also be informative: (Ars Technica)
Does the local method timeIntervalSinceData return an "NSTimeInterval" (typdef double)? I'm guessing it doesn't, hence the error - but the code isn't here to see.
We need to see a bit more code to help you - but the D_DAY undeclared should be easy to resolve. Assuming it's not a #define somewhere in your headers, you need to specify what it is in that function or higher up in the file. I'm guessing you're just missing a #define somewhere that puts in a specific value - at least by syntax.
The warning is because of this error - the parser doesn't know how to complete things nicely until you've fixed that.
for 1) i think it talks about "self" ;it is a date?

if-query: if (Nslog isEqualtoString #"...") - How can I make this?

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.