Why is the Ejectable property of a DADisk always false? - objective-c

I'm trying to get a list of all ejectable disks using the Disk Arbitration framework. The problem is that the Ejectable property is always false (even when diskutil info says Ejectable: Yes). What's going on? Do I need to do a DADiskClaim first or something?
- (NSArray *)disks {
NSMutableArray *disks = #[].mutableCopy;
// Get a list of everything in /Volumes/
NSArray *volumes = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"/Volumes/" error:NULL];
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
// Use NSSet for easy object finding
NSMutableSet *bsdNamesOfDisks = #[].mutableCopy;
for (NSString *volume in volumes) {
// Create the URL for the volume mount point
NSURL *volumeURL = [NSURL URLWithString:[[NSString stringWithFormat:#"/Volumes/%#", volume] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
// Create DADisk for the volume
DADiskRef volumeDisk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, (__bridge CFURLRef)volumeURL);
// Filter out files/directories that aren't volumes
if (volumeDisk) {
// Get disk description
NSDictionary *description = (__bridge_transfer NSDictionary *)DADiskCopyDescription(volumeDisk);
// Get BSD name and ejectable property
NSString *bsdName = description[(__bridge id)kDADiskDescriptionMediaBSDNameKey];
CFBooleanRef ejectable = (__bridge CFBooleanRef)description[(__bridge id)kDADiskDescriptionMediaEjectableKey];
// Check to see if this is a volume we're interested in
if (bsdName && ejectable && CFBooleanGetValue(ejectable)) {
// Deletes "disk" in "disk0s1" and separates the numbers
NSArray *numbersInName = [[bsdName substringFromIndex:4] componentsSeparatedByCharactersInSet:[NSCharacterSet letterCharacterSet]];
// Scan the first number
NSScanner *scanner = [NSScanner scannerWithString:numbersInName[0]];
NSInteger diskNumber;
[scanner scanInteger:&diskNumber];
// Construct BSD name for the whole disk
NSString *bsdDiskName = [NSString stringWithFormat:#"disk%ld", diskNumber];
// Check to see if we already added this disk to our array (e.g. disk0s1 and disk0s2 both give disk0)
if (![bsdNamesOfDisks containsObject:bsdDiskName]) {
// Create DADisk for the whole disk
DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, bsdDiskName.UTF8String);
// Add DADisk to disk array and BSD name to name array
[disks addObject:(__bridge_transfer id)disk];
[bsdNamesOfDisks addObject:bsdDiskName];
}
}
CFRelease(volumeDisk);
}
}
CFRelease(session);
return disks.copy;
}

Related

Getting out of memory even with release while reading file in chunks to NSData

I'm reading a file on my disk (which can be few GB in size) in 10MB chunks to verify MD5 for it. Method fetchRecords has been simplified as it is a bi
t long. The problem is that the data is released when fetchRecords method returns, by then I have few GB in memory. If file is big enough, it causes a crash. [dataChunk release] at the end does not help. Getting a lot of inactive memory until it returns.
- (void)fetchRecords
{
for (DownloadChunkInfo *downloadChunkInfo in [downloadFileInfo chunk])
{
NSData *dataChunk = [NSData dataWithContentsOfFile:fileDownloadPath withStartOffset:[downloadChunk startingByte] andEndOffset:[downloadChunk endingByte]];
if ([dataChunk length] == [downloadChunk length])
{
if ([downloadChunk md5] && [[dataChunk MD5] isEqualToString:[downloadChunk md5]])
{
// Some code
}
else
{
// Some code
}
}
[dataChunk release];
}
}
+ (NSData *)dataWithContentsOfFile:(NSString *)path withStartOffset:(off_t)startOffset andEndOffset:(off_t)endOffset
{
FILE *file = fopen([path UTF8String], "rb");
if(file == NULL)
return nil;
uint64_t size = (endOffset - startOffset) + 1;
void *data = malloc(size); // check for NULL!
fseeko(file, startOffset, SEEK_SET);
size_t realSize = fread(data, 1, size, file); // check return value, in case read was short!
fclose(file);
// NSData takes ownership and will call free(data) when it's released
return [NSData dataWithBytesNoCopy:data length:realSize];
}
[dataChunk release] is actually wrong, because you don't "own" the object returned by
NSData *dataChunk = [NSData dataWithContentsOfFile:...];
The return value is (subject to possible optimizations made by the compiler)
an "autoreleased" object, which is released only when the current autorelease pool
is destroyed.
Therefore, using a local autorelease pool should help:
for (DownloadChunkInfo *downloadChunkInfo in [downloadFileInfo chunk])
{
#autoreleasepool {
NSData *dataChunk = [NSData dataWithContentsOfFile:fileDownloadPath withStartOffset:[downloadChunk startingByte] andEndOffset:[downloadChunk endingByte]];
// do something with dataChunk ...
}
}
For more information, see
"Basic Memory Management Rules"
"Using Autorelease Pool Blocks"
in the "Advanced Memory Management Programming Guide" for more information.

Parsing a JSON of unknown Structure Objective-C

I am trying to parse this json and am not sure of parsing this because the keys like "vis-progress-control" might change and I am trying to build a general code which parses this type of a json. I am sure that "type" key will be present in the json structure.
NSDictionary *dict = [sData JSONValue];
NSArray *items = [dict valueForKeyPath:#"assets"];
NSLog(#"%#", items);
for (NSString *key in [[dict objectForKey:#"assets"]allKeys]) {
for (NSString *subKey in [[[dict objectForKey:#"assets"] objectForKey:key]allKeys]) {
NSLog(#"Value at subkey:%# is %#\n",subKey,[[[dict objectForKey:#"assets"]objectForKey:key]objectForKey:subKey]);
}
}
I am using the SBJson Library on Github. My actual issue is How do I access "direction", "degrees" etc when I do not know the "vjs-progress-holder" key?
I have also a widgets array nested within a widgets array. How do I get these values as well?
Read about tree traversal. Sounds like you want to traverse the "tree" of nodes and when you find a particular leaf value you will then traverse back up one level and know the parent is the container you want.
So the idea is that once it's parsed from the JSON, forget it's JSON because now it's just a tree in arrays and dictionaries. Traverse it by getting all the keys in the dictionary via allKeys (returns an array of keys) and then iterate through them getting the associated values (using something like (psuedo code):
for ( NSString * key in allkeysarray) {
NSString * val = [dict objectForKey: key];
if ( [val isEqualToString: #"gradient"] )
{
// now you know that this dictionary is the one you're looking for so you can maybe break out of this loop and just use the keys you know reference these values.
break;
}
}
hopefully that's enough to get you going.
Assuming I'm understanding your objective here, it seems like you want to do something like this?
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
float degrees = [asset[#"degrees"] floatValue];
// and read whatever other values you need for gradients
}
if ([type isEqualToString:#"image"]) {
// and read the appropriate values for images here...
}
}
So I'll make a different assumption here, which is that you want an array of gradients. So then that looks basically like this:
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
NSMutableArray *gradients = [NSMutableArray array];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
// Add this asset to the list of gradients:
[gradients addObject:asset];
}
if ([type isEqualToString:#"image"]) {
// do something similar for images
}
}
Then, after having done that, if you would like to read the "degrees" field for the 4th gradient, for example, you will find it at gradients[3][#"degrees"]

Is it possible to glob a directory in iOS and have the results cached by the OS?

Many methods of reading from a filesystem using NSFileManager and lower level APIs on iOS involve built-in caching, so reading from directories that haven't changed can be quite fast, even if there's lots of items in the directories.
I have a situation where I want to be able to enumerate files in a directory using a glob:
i.e. the folder has files named like this:
1-1-0.png
1-2-0.png
1-3-0.png
1-3-1.png
2-2-1.png
5-1-1.png
5-1-2.png
5-2-1.png
5-3-0.png
6-1-1.png
...
1501-5-2.png
I might want to get all filenames matching 5-*-1.png, which would give me back 5-1-1.png and 5-2-1.png.
Loading the whole directory listing then doing the globbing in RAM is pretty straightforward, but is there a method for doing this at an OS level that would have caching built-in, so repeated calls to the same glob would give cached (faster) results?
You can use the glob() function as outlined in this gist: https://gist.github.com/bkyle/293959
#include <glob.h>
+ (NSArray*) arrayWithFilesMatchingPattern: (NSString*) pattern inDirectory: (NSString*) directory {
NSMutableArray* files = [NSMutableArray array];
glob_t gt;
NSString* globPathComponent = [NSString stringWithFormat: #"/%#", pattern];
NSString* expandedDirectory = [directory stringByExpandingTildeInPath];
const char* fullPattern = [[expandedDirectory stringByAppendingPathComponent: globPathComponent] UTF8String];
if (glob(fullPattern, 0, NULL, &gt) == 0) {
int i;
for (i=0; i<gt.gl_matchc; i++) {
size_t len = strlen(gt.gl_pathv[i]);
NSString* filename = [[NSFileManager defaultManager] stringWithFileSystemRepresentation: gt.gl_pathv[i] length: len];
[files addObject: filename];
}
}
globfree(&gt);
return [NSArray arrayWithArray: files];
}

Can not change/replace NSDictionary Key value IOS

I am downloading a file revision status from Dropbox and basically I compare downloaded revison number in Dropbox with revision number in my local plist.
After comparing them I want to change local revision number with Dropbox's revision number. But it is not working I am about to loss my mind.
I put some flags and NSlogs it seems it replace the value but after I call same function or launch the app again I see that value is not replaced. It gives the same output over and over again
NSString* revisionLocal = [dicInner objectForKey:#"revision"];
NSString* statusLocal = [dicInner objectForKey:#"status"];
NSLog(#"revision value before %#",revisionLocal);
NSLog(#"status value before %#",statusLocal);
//If revision has changed on dropbox, flag it as outdated on the local revision
if(![revisionLocal isEqualToString: dropBoxRevision] ){
[dicInner setValue:#"outdated" forKey:#"status"];
//But the revision is the latest
[dicInner setValue:dropBoxRevision forKey:#"revision"];
//[dicInner setValue:#"outdated" forKey:#"revision"];
NSLog(#"revision value %#",[dicInner objectForKey:#"revision"]);
NSLog(#"status value %#",[dicInner objectForKey:#"status"]);
so this give me the output of:
revision value before 4309efbbb7
status value before updated
revision value 4409efbbb7
status value outdated
And the full code is:
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
//get the local revision
NSDictionary * localRevisionDic = [FileUtils readPlistIntoDictionary:#"revision.plist"];
NSString *fileRevString = [NSString alloc];
//get the revision from Dropbox
//NSString * dropboxRevision;
if (metadata.isDirectory) {
NSLog(#"Folder '%#' contains:", metadata.path);
for (DBMetadata *file in metadata.contents) {
NSLog(#"\t%#", file.filename);
//NSLog(#"\t%#", file.lastModifiedDate);
NSLog(#"\t%#", file.rev );
//Assign dropbox revision for this file
//dropboxRevision = file.rev;
//if no local revision.plist, entry will be added. Hence init here
if (localRevisionDic==nil){
localRevisionDic = [[NSMutableDictionary alloc]init];
}
//Otherwise go through each from dropbox and campare with local
//From Dropbox
NSString * dropBoxFileName = file.filename;
NSString * dropBoxRevision = file.rev;
fileRevString = file.rev;
//if no local revision.plist entry is added for all other files
//with status need_downloaded, and no revision
if ([localRevisionDic count]==0){
//Creating revision dictionary entry for agenda.plist
NSDictionary * localRevisionDicDic = [[NSMutableDictionary alloc]init];
//when agenda.plist revision entry is added update the revision while leaving status as "new" before downloading
//will be updated accordingly if download fails
[localRevisionDicDic setValue:#"new" forKey:#"status"];
//Status is new but the revision is the latest
[localRevisionDicDic setValue:dropBoxRevision forKey:#"revision"];
[localRevisionDic setValue:localRevisionDicDic forKey:dropBoxFileName];
}else{//If there is local revision.plist compare and update accordingly
NSDictionary * dicInner = [localRevisionDic objectForKey:dropBoxFileName];
//File name Found locally
if (dicInner!=nil){
NSString* revisionLocal = [dicInner objectForKey:#"revision"];
NSString* statusLocal = [dicInner objectForKey:#"status"];
NSLog(#"revision value before %#",revisionLocal);
NSLog(#"status value before %#",statusLocal);
//If revision has changed on dropbox, flag it as outdated on the local revision
if(![revisionLocal isEqualToString: dropBoxRevision] ){
[dicInner setValue:#"outdated" forKey:#"status"];
//But the revision is the latest
[dicInner setValue:dropBoxRevision forKey:#"revision"];
//[dicInner setValue:#"outdated" forKey:#"revision"];
NSLog(#"revision value %#",[dicInner objectForKey:#"revision"]);
NSLog(#"status value %#",[dicInner objectForKey:#"status"]);
}
}else{//File name not found locally newly added on dropbox
NSDictionary * localRevisionDicDic = [[NSMutableDictionary alloc]init];
//when agenda.plist revision entry is added update the revision while leaving status as "new" before downloading
//will be updated accordingly if download fails
[localRevisionDicDic setValue:#"new" forKey:#"status"];
//But the revision is the latest
[localRevisionDicDic setValue:dropBoxRevision forKey:#"revision"];
[localRevisionDic setValue:localRevisionDicDic forKey:dropBoxFileName];
}
}
}
}
//At this point agendaRevisionDicTemp contains all the files in dropbox entered/updated.
[[self agenda] setRevision:localRevisionDic];
//*****The following block is needed to determine is new agenda is needed or not.
BOOL newAgendaNeeded = false;
NSMutableDictionary * agendaRevisionDicLocal = [localRevisionDic objectForKey:#"agenda.plist"];
//NSString * localRevision = [agendaRevisionDicLocal objectForKey:#"revision"]; //what is this value?
NSString * localStatus = [agendaRevisionDicLocal objectForKey:#"status"];
NSLog(#"Local Status= %#",agendaRevisionDicLocal);
if ([localStatus isEqualToString:#"new"] ||[localStatus isEqualToString:#"outdated"]){
newAgendaNeeded = true;
//when agenda.plist is added update the revision while leaving status as "new" before downloading
//will be updated accordingly if download fails
NSDictionary * agendaDic = [[[self agenda]revision] objectForKey:#"agenda.plist"];
[agendaDic setValue:#"updated" forKey:#"status"];
NSLog(#"agendaDic where update %#",agendaDic);
}
//*****The above block is needed to determine is new agenda is needed or not.
//If new agenda is needed download
if (newAgendaNeeded){
//Download agenda.plist
NSString *documentsDirectory = FileUtils.getDocumentsDirectory;
[[self restClient] loadFile:#"/agenda.plist" intoPath: [ NSString stringWithFormat:#"%#/%#",documentsDirectory,#"agenda.plist"] ];
} else{//Else display the scene
[self populateSceneFromAgenda];
}
[restOfView reloadData];
// Add at start of requestFinished AND requestFailed
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
Any Idea How Can I replace that value?
You don't respect immutability of your NSDictionary objects. You declare localRevisionDic as NSDictionary*, but later it could be assigned value of NSMutableDictionary. localRevisionDicDic declared as NSDictionary* but initialised with value of NSMutableDictionary. Following two assignments should be warned at compile time. At line
[localRevisionDic setValue:localRevisionDicDic forKey:dropBoxFileName];
who knows for sure is localRevisionDic mutable or immutable one?
Then, again, you declare dicInner as NSDictionary*, but try to setValue twice later. Make mutableCopy of dictionary first.
I guess your dictionary changes correctly whats wrong with your code should be that you do not save your dictionary to the plist you edit
In fact there is deffinetly something wrong with that method[[self agenda] setRevision:localRevisionDic];
Try to add following code below from [[self agenda] setRevision:localRevisionDic];
//write dictionary to plist
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"revision.plist"];
// write plist to disk
[localRevisionDic writeToFile:path atomically:YES];
// read it back in with different dictionary variable
NSMutableDictionary *savedStock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
if( savedStock==nil ){
NSLog(#"failed to retrieve dictionary from disk");
}

Retrieving values from json using objective-c

I am currently trying to work with json and objective-c however having a bit of difficulty. The following is the json that is being returned
{
sethostname = (
{
msgs = "Updating Apache configuration\nUpdating cPanel license...Done. Update succeeded.\nBuilding global cache for cpanel...Done";
status = 1;
statusmsg = "Hostname Changed to: a.host.name.com";
warns = (
);
});
}
I am able to check that the response is coming back and the key is sethostname however no matter what I try I cannot get for example the value of status or statusmsg. Can anyone point me in the right location. The following is basic code I am using to check that sethostname is returned.
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&myError];
NSLog([res description]);
NSArray *arr;
arr = [res allKeys];
if ([arr containsObject:#"sethostname"])
{
NSLog(#"worked");
}
When in doubt, write down the structure of your JSON data. For example:
{
sethostname = (
{
msgs = "Updating Apache configuration\nUpdating cPanel license...Done. Update succeeded.\nBuilding global cache for cpanel...Done";
status = 1;
statusmsg = "Hostname Changed to: a.host.name.com";
warns = (
);
});
}
(which is in NeXTSTEP property list format, actually) means that you have a top-level dictionary. This top-level dictionary contains a key called sethostname whose value is an array. This array is comprised of dictionaries, each dictionary having a set of keys: msgs, status, statusmsg, warns. msgs has a string value, status has a number value, statusmsg has a string value,warns` has an array value:
dictionary (top-level)
sethostname (array of dictionaries)
dictionary (array element)
msgs (string)
status (number)
statusmsg (string)
warns (array)
??? (array element)
Having understood this structure, your code should look like:
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&myError];
if (!res) { // JSON parser failed }
// dictionary (top-level)
if (![res isKindOfClass:[NSDictionary class]]) {
// JSON parser hasn't returned a dictionary
}
// sethostname (array of dictionaries)
NSArray *setHostNames = [res objectForKey:#"sethostname"];
// dictionary (array element)
for (NSDictionary *setHostName in setHostNames) {
// status (number)
NSNumber *status = [setHostName objectForKey:#"status"];
// statusmsg (string)
NSString *statusmsg = [setHostName objectForKey:#"statusmsg"];
…
}
Why not use the simplest JSON method - [myString jsonValue];
It's part of this JSON framework for objective-c
I don't think if ([arr containsObject:#"sethostname"]) is going to work, because the results array is not going to contain that exact object. It might contain an object with the same content, but it won't be the SAME object.
As jtbandes wrote, you need to log the actually output. NSLog both res and arr and see what you have.