I am using the libical library to parse the iCalendar format and read the information I need out of it. It is working absolutely fine so far, but there is one odd thing concerning ical.
This is my code:
icalcomponent *root = icalparser_parse_string([iCalData cStringUsingEncoding:NSUTF8StringEncoding]);
if (root)
{
icalcomponent *currentEvent = icalcomponent_get_first_component(root, ICAL_VEVENT_COMPONENT);
while (currentEvent)
{
while(currentProperty)
{
icalvalue *value = icalproperty_get_value(currentProperty);
char *icalString = icalvalue_as_ical_string_r(value); //seems to leak
NSString *currentValueAsString = [NSString stringWithCString:icalString
encoding:NSUTF8StringEncoding];
icalvalue_free(value);
//...
//import data
//...
icalString = nil;
currentValueAsString = nil;
icalproperty_free(currentProperty);
currentProperty = icalcomponent_get_next_property(currentEvent, ICAL_ANY_PROPERTY);
} //end while
} //end while
icalcomponent_free(currentEvent);
}
icalcomponent_free(root);
//...
I did use instruments to check my memory usage and were able to find out, that this line seems to leak:
char *icalString = icalvalue_as_ical_string_r(value); //seems to leak
If I'd copy and paste this line 5 or six times my memory usage would grow about 400kb and never get released anymore.
There is no free method for the icalvalue_as_ical_string_r method because it's returning a char *..
Any suggestions how to solve this issue? I would appreciate any help!
EDIT
Taking a look at the apple doc says the following:
To get a C string from a string object, you are recommended to use UTF8String. This returns a const char * using UTF8 string encoding.
const char *cString = [#"Hello, world" UTF8String];
The C string you receive is owned by a temporary object, and will become invalid when automatic deallocation takes place. If you want to get a permanent C string, you must create a buffer and copy the contents of the const char * returned by the method.
But how to release a char * string properly now if using arc?
I tried to add #autorelease {...} in front of my while-loop but without any effort. Still increasing memory usage...
Careful with the statement "no free method...because it's returning a char*"; that is never something you can just assume.
In the absence of documentation you can look at the source code of the library to see what it does; for example:
http://libical.sourcearchive.com/documentation/0.44-2/icalvalue_8c-source.html
Unfortunately this function can do a lot of different things. There are certainly some cases where calling free() on the returned buffer would be right but maybe that hasn't been ensured in every case.
I think it would be best to request a proper deallocation method from the maintainers of the library. They need to clean up their own mess; the icalvalue_as_ical_string_r() function has at least a dozen cases in a switch that might have different deallocation requirements.
icalvalue_as_ical_string_r returns a char * because it has done a malloc() for your result string. If your pointer is non-NULL, you have to free() it after use.
Related
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
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.
I want to convert a NSString into a const char * in order to access a sqlite DB.
This works:
NSString *queryStatementNS = #"select title from article limit 10";
const char *queryStatement = [queryStatementNS UTF8String];
This causes a crash in the simulator (without any stacktrace):
NSString *queryStatementNS = [NSString stringWithFormat:#"select title from article limit %d", 10];
const char *queryStatement = [queryStatementNS UTF8String];
Can anybody tell me, what the stringWithFormat method changes in the String to make the conversion to UTF8 (or to ASCII using cStringUsingEncoding:NSASCIIStringEncoding) crash? The same crash happens also when passing no arg at all to the stringWithFormat. Could it be related to memory management somehow?
From the documentation:
The returned C string is automatically
freed just as a returned object would
be released; you should copy the C
string if it needs to store it outside
of the autorelease context in which
the C string is created.
Your problem is that queryStatement is being freed when queryStatementNS gets deallocated, and as queryStatementNS is autoreleased you don't know exactly when this is going to occur. You can either retain queryStatementNS by calling
[queryStatementNS retain]
at some point in that function (remember to release it when you want to relinquish ownership), you can explicitly create a non-autoreleased string to deal with yourself by saying
NSString* query = [[NSString alloc] initWithFormat:#"a string! %d", 10, nil]
(as an aside, note the nil - if you don't have it there xcode will give you a missing sentinel warning)
or you can copy the output of [queryStatementNS UTF8String] to your const char* queryStatement as you would in plain C, with strcopy or whatever.
The reason the first example you give continues to work is that you're setting a pointer to a string literal, #"select title from article limit 10". The objective c compiler ensures that there's only ever one instance of this string in memory, no matter how many times you reference it in your code. Thus, it doesn't obey the standard memory management conventions of objective c and your pointer remains valid outside of the autoreleased context.
When converting a NSString to a char* using UTF8String, how to retain it?
According to the following link, when you use UTF8String the returned char* is pretty much autoreleased, so it won't last beyond the current function: http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSString_Class/Reference/NSString.html#jumpTo_128
It says i have to copy it or something to keep it around. How can i do this?
The reason i ask is that if i do [myCharPointer retain] it doesn't retain it because it's not an obj-c object, it's a c pointer.
Thanks
You can use strdup()
const char* utf8Str = [#"an example string" UTF8String];
if (utf8Str != NULL)
{
stringIWantToKeep = strdup(utf8Str);
}
When you are done with stringIWantToKeep, you free it as though it was originally malloc'd.
Try using -getCString:maxLength:encoding:, e.g.:
NSUInteger bufferCount = sizeof(char) * ([string length] + 1);
const char *utf8Buffer = malloc(bufferCount);
if ([string getCString:utf8Buffer
maxLength:bufferCount
encoding:NSUTF8StringEncoding]) {
NSLog("Success! %s", utf8Buffer);
free(utf8Buffer); // Remember to do this, or you will get a memory leak!
}
If you need an object to retain/release as every other object you can do the following:
NSData* storage= [yourNSString dataUsingEncoding:NSUTF8StringEncoding];
char* yourCString= (char*)storage.bytes;
// your code flow here
Now you can pass both storage and yourCString to any function retaining/releasing storage as you like, when the retain count of storage will go to 0 the memory pointed by yourCString will be freed too.
I used this to keep a single copy of a very long string while having references created by strtok_r in beans and have the memory released only when all beans had been released.
A little note about (missing) BOM:
As stated here http://boredzo.org/blog/archives/2012-06-03/characters-in-nsstring dataUsingEncoding: should add BOM but i've checked with the debugger and there is no BOM in the returned bytes.
Based on this SO question asked a few hours ago, I have decided to implement a swizzled method that will allow me to take a formatted NSString as the format arg into stringWithFormat, and have it not break when omitting one of the numbered arg references (%1$#, %2$#)
I have it working, but this is the first copy, and seeing as this method is going to be potentially called hundreds of thousands of times per app run, I need to bounce this off of some experts to see if this method has any red flags, major performance hits, or optimizations
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {
if (format != nil) {
va_list args;
va_start(args, format);
// $# is an ordered variable (%1$#, %2$#...)
if ([format rangeOfString:#"$#"].location == NSNotFound) {
//call apples method
NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
va_end(args);
return s;
}
NSMutableArray *newArgs = [NSMutableArray arrayWithCapacity:NUMARGS(args)];
id arg = nil;
int i = 1;
while (arg = va_arg(args, id)) {
NSString *f = [NSString stringWithFormat:#"%%%d\$\#", i];
i++;
if ([format rangeOfString:f].location == NSNotFound) continue;
else [newArgs addObject:arg];
}
va_end(args);
char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
[newArgs getObjects:(id *)newArgList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
free(newArgList);
return result;
}
return nil;
}
The basic algorithm is:
search the format string for the %1$#, %2$# variables by searching for %#
if not found, call the normal stringWithFormat and return
else, loop over the args
if the format has a position variable (%i$#) for position i, add the arg to the new arg array
else, don't add the arg
take the new arg array, convert it back into a va_list, and call initWithFormat:arguments: to get the correct string.
The idea is that I would run all [NSString stringWithFormat:] calls through this method instead.
This might seem unnecessary to many, but click on to the referenced SO question (first line) to see examples of why I need to do this.
Ideas? Thoughts? Better implementations? Better Solutions?
Whoa there!
Instead of screwing with a core method that you very probably will introduce subtle bugs into, instead just turn on "Static Analyzer" in your project options, and it will run every build - if you get the arguments wrong it will issue a compiler warning for you.
I appreciate your desire to make the application more robust but I think it very likely that re-writing this method will more likely break your application than save it.
How about defining your own interim method instead of using format specifiers and stringWithFormat:? For example, you could define your own method replaceIndexPoints: to look for ($1) instead of %1$#. You would then format your string and insert translated replacements independently. This method could also take an array of strings, with NSNull or empty strings at the indexes that don't exist in the “untranslated” string.
Your method could look like this (if it were a category method for NSMutableString):
- (void) replaceIndexPointsWithStrings:(NSArray *) replacements
{
// 1. look for largest index in "self".
// 2. loop from the beginning to the largest index, replacing each
// index with corresponding string from replacements array.
}
Here's a few issues that I see with your current implementation (at a glance):
The __VA_ARGS__ thingy explained in the comments.
When you use while (arg = va_arg(args, id)), you are assuming that the arguments are nil terminated (such as for arrayWithObjects:), but with stringWithFormat: this is not a requirement.
I don't think you're required to escape the $ and # in your string format in your arg-loop.
I'm not sure this would work well if uaStringWithFormat: was passed something larger than a pointer (i.e. long long if pointers are 32-bit). This may only be an issue if your translations also require inserting unlocalised numbers of long long magnitude.