Objective C: Why is this code leaking? - objective-c

I'm trying to implement a method similar to what mytunescontroller uses to check if it has been added to the user's login items. This code compiles without warnings but if I run the leaks performance tool I get the following leaks:
Leaked Object # Address Size Responsible Library Responsible Frame
NSURL 7 < multiple > 448 LaunchServices LSSharedFileListItemGetFSRef
NSCFString 6 < multiple > 432 LaunchServices LSSharedFileListItemGetFSRef
Here is the culprit:
- (BOOL)isAppStartingOnLogin
{
LSSharedFileListRef loginListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginListRef) {
NSArray *loginItemsArray = (NSArray *)LSSharedFileListCopySnapshot(loginListRef, NULL);
NSURL *itemURL;
for (id itemRef in loginItemsArray) {
if (LSSharedFileListItemResolve((LSSharedFileListItemRef)itemRef, 0, (CFURLRef *) &itemURL, NULL) == noErr) {
if ([[itemURL path] hasPrefix:[[NSBundle mainBundle] bundlePath]]) {
[loginItemsArray release];
[itemURL release];
CFRelease(loginListRef);
return YES;
}
}
}
[itemURL release];
[loginItemsArray release];
CFRelease(loginListRef);
}
return NO;
}

LSSharedFileListItemResolve() returns an owned object in the third parameter. This can be verified by reading the header. As a result, you need to release itemURL.

Related

How to parse and take only this string value

I wanted to get only array string value app. As example(SLGoogleAuth ,HalfTunes,TheBackgrounder,Calculiator) . But don't know how to do?
It's a code.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSLog(#"apps: %#", [workspace performSelector:selectorALL]);
}
It's output:
Thanks in advance
You do not want to parse that. NSLog prints out a description of an object. You want to access that value directly.
[LSApplicationWorkspace allApplications];
returns NSArray of LSApplicationProxy. LSApplicationProxy class has a ivar _bundleURL that contains information that you need. You need runtime functions to access it. Working example below:
// #import <objc/runtime.h>
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSArray* appProxies = [workspace performSelector:selectorALL];
Ivar bundleUrlIvar = class_getInstanceVariable([appProxies.firstObject class], "_bundleURL");
NSMutableString* result = [NSMutableString string];
for (id appProxy in appProxies)
{
NSURL* url = object_getIvar(appProxy, bundleUrlIvar);
// at this point you have the information and you can do whatever you want with it
// I will make it a list as you asked
if (url)
{
[result appendFormat:#",%#", [url lastPathComponent]];
}
}
if (result.length > 0)
{
// remove comma from beginning of the list
[result deleteCharactersInRange:NSMakeRange(0, 1)];
}
NSLog(#"apps: %#", result);
Note that this will be rejected by AppStore as you are using private apis. So use at your own discretion.

Getting graphic card information in objective C

I need to get Graphics card information in my application. The information I need is the same as displayed by *system_profiler SPDisplays* command under the section Graphics/Displays:.
I have already considered using sysctl(), but I am unable to find proper hardware selector for graphics card in sysctl.h
Any suggestions highly appericiated.
After fiddling with IOKit I have managed to fetch the required information. The code can be seen here (original source)and below:
- (void)displayGraphicsInfo
{
// Get dictionary of all the PCI Devicces
CFMutableDictionaryRef matchDict = IOServiceMatching("IOPCIDevice");
// Create an iterator
io_iterator_t iterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,matchDict,
&iterator) == kIOReturnSuccess)
{
// Iterator for devices found
io_registry_entry_t regEntry;
while ((regEntry = IOIteratorNext(iterator))) {
// Put this services object into a dictionary object.
CFMutableDictionaryRef serviceDictionary;
if (IORegistryEntryCreateCFProperties(regEntry,
&serviceDictionary,
kCFAllocatorDefault,
kNilOptions) != kIOReturnSuccess)
{
// Service dictionary creation failed.
IOObjectRelease(regEntry);
continue;
}
const void *GPUModel = CFDictionaryGetValue(serviceDictionary, #"model");
if (GPUModel != nil) {
if (CFGetTypeID(GPUModel) == CFDataGetTypeID()) {
// Create a string from the CFDataRef.
NSString *modelName = [[NSString alloc] initWithData:
(NSData *)GPUModel encoding:NSASCIIStringEncoding];
NSLog(#"GPU Model: %#", modelName);
[modelName release];
}
}
// Release the dictionary
CFRelease(serviceDictionary);
// Release the serviceObject
IOObjectRelease(regEntry);
}
// Release the iterator
IOObjectRelease(iterator);
}
}

Objective-C, Authorization fails to perform function

I'm trying to create an Authorization to copy a file using SMJobBless, although I can't get it to work. The helper app is successfully authorized and the Job is available! message appears before the [self copyFile] method, but the copyFile always fails. If someone could shed some light on what I'm doing wrong or provide and example of how to make this work that would be great.
appDelegate.h
#import <Cocoa/Cocoa.h>
#interface SMJobBlessAppController : NSObject {
IBOutlet NSTextField *_textField;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
- (void)copyFile;
#end
appDelegate.m
#import <ServiceManagement/ServiceManagement.h>
#import <Security/Authorization.h>
#import "appDelegate.h"
#implementation SMJobBlessAppController
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSError *error = nil;
if (![self blessHelperWithLabel:#"com.apple.bsd.SMJobBlessHelper" error:&error]) {
NSLog(#"Something went wrong!");
} else {
/* At this point, the job is available. However, this is a very
* simple sample, and there is no IPC infrastructure set up to
* make it launch-on-demand. You would normally achieve this by
* using a Sockets or MachServices dictionary in your launchd.plist.
*/
NSLog(#"Job is available!");
[self->_textField setHidden:false];
[self copyFile];
}
}
- (void)copyFile {
NSError *error = nil;
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *sourceFile = #"~/path/to/file.txt";
NSString *destFile = #"~/Library/Application Support/myApp/file.txt";
if ([fileManager copyItemAtPath:sourceFile toPath:destFile error:&error] == YES) {
NSLog (#"[FILE] Copied.");
// NSLog (#"Copy successful");
} else {
NSLog (#"[FILE] Copy failed.");
NSLog (#" %# %#",sourceFile, destFile);
// NSLog (#"Copy failed");
}
[fileManager release];
return;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
{
BOOL result = NO;
AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
AuthorizationRef authRef = NULL;
/* Obtain the right to install privileged helper tools (kSMRightBlessPrivilegedHelper). */
OSStatus status = AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef);
if (status != errAuthorizationSuccess) {
NSLog(#"Failed to create AuthorizationRef, return code %i", status);
} else {
/* This does all the work of verifying the helper tool against the application
* and vice-versa. Once verification has passed, the embedded launchd.plist
* is extracted and placed in /Library/LaunchDaemons and then loaded. The
* executable is placed in /Library/PrivilegedHelperTools.
*/
result = SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, authRef, (CFErrorRef *)error);
}
return result;
}
#end
You're totally missing the point of SMJobBless. It doesn't magically make your current app able to do privileged things. Instead, it installs and runs a separate helper tool, which is allowed to do privileged things, but should do nothing else (as little as possible).
You need to move your code in copyFile to the main function in SMJobBlessHelper.c. (And since that's a C file, you'll have to either rewrite it in C -- perhaps using CoreFoundation -- or you'll have to change the tool to use Objective-C. Nobody said this would be easy.)

Calling two times a function of a static variable?

I have this class containing a static variable "database" which represent a database realized with sqlite and a function getAllShop which task is recalling all the data that are stored in the db and filling them into a mutable array
#define kFilename #"negozi.sqlite"
#implementation ShopDatabase
static ShopDatabase *database;
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
return database;
}
}
- (id)init
{
self = [super init];
if (self) {
// Create the path to the database in the Documents directory for the bundle itself is not writable
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex:0];
databasePath = [documentsDirectory stringByAppendingPathComponent:kFilename];
if (![[NSFileManager defaultManager] isReadableFileAtPath:databasePath]) {
if ([[NSFileManager defaultManager] copyItemAtPath:yourOriginalDatabasePath toPath:databasePath error:NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, databasePath);
}
// --- open db
if(sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK){
NSLog(#"Failed to open database");
}else {
NSLog(#"Database opened");
}
}
return self;
}
- (NSMutableArray *) getAllShops{
// ------ read all the db
NSMutableArray *returnArray=[[NSMutableArray alloc] init];
NSString *query= #"SELECT * FROM negozio";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, NULL) == SQLITE_OK){
NSLog(#"Prepared database");
while (sqlite3_step(statement)==SQLITE_ROW) {
int uniqueId = sqlite3_column_int(statement, 0);
NSMutableString *nome = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
ShopInfo *info= [[ShopInfo alloc] initWithUniqueId:uniqueId nome:nome];
[returnArray addObject:info];
}
sqlite3_finalize(statement);
}
return returnArray;
}
#end
When i have to take data from the database from another class i do this, calling the getAllShop and all goes well. In this way i have all the data of the db into my array shopinfo:
NSMutableArray *shopInfo=[[ShopDatabase database] getAllShops];
Now, my database contains data that i need to use to fill TWO table view, so i need to execute this TWO times: one time in the class representing the first table view and one in the second. When i do this in the first view all goes well, but when i do the same the second time, Xcode give me a exc bad access error. I tried executing the code two times in the same class and this is what i get
2012-05-11 13:06:54.897 Shopping Mall[11333:707] -[NegozioPartenza getAllShops]: unrecognized selector sent to instance 0x14b8c0
2012-05-11 13:06:54.899 Shopping Mall[11333:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NegozioPartenza getAllShops]: unrecognized selector sent to instance 0x14b8c0'
*** First throw call stack:
(0x33ad188f 0x325c3259 0x33ad4a9b 0x33ad3915 0x33a2e650 0xa4141 0x35727e33 0x3574c629 0x35710d7d 0x357d34dd 0x3571955d 0x3571940b 0x357d34a3 0x35788873 0x357881b7 0x357d1d89 0x357d04eb 0x3582b82b 0x33a2b3fd 0x35709faf 0x35709f6b 0x35709f49 0x35709cb9 0x3570a5f1 0x35708ad3 0x357084c1 0x356ee83d 0x356ee0e3 0x32fa622b 0x33aa5523 0x33aa54c5 0x33aa4313 0x33a274a5 0x33a2736d 0x32fa5439 0x3571ce7d 0xa2515 0xa24c0)
terminate called throwing an exception(lldb)
I am a newbie to objective C so i can't manage to understand what's the point. How can i call two times a function on a static variable? Thank you.
Edit: maybe calling the [ShopDatabase database] activate a second time the constructor of initializazione making mess? When i say that a variable is static it mean there's only one instance of it for every object of that class, right? So how i can access that unique instance after creating it the first time? I think i'm messing up what happen when you use a static variable...
You don't have a valid return on the second call.
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
return database;
}
}
On the second call database is not nil and you don't return anything. You should be getting a warning that not all control paths return a value.
Here is the proper way.
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
}
return database;
}
Somewhere after your first call, database is being released. Subsequently, an object of type NegozioPartenza is being created at the same location. The second call to [ShopDatabase database] returns this object, and you then send -getAllShops to that object, which obviously doesn't implement that method.

Recommended way to copy arbitrary files using Cocoa

I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.
Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.
Optional: Which command line tool provides help and could be utilized by a Cocoa application.
NSFileManager is your friend:
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:#"path/to/source" toPath:#"path/to/destination" error:&error])
{
// copy succeeded
}
else
{
// copy failed, print error
}
You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.
This sample shows how to copy and move both files and folders. It
shows both the synchronous and asynchronous (using CFRunLoop) use of
the FSFileOperation APIs. In addition, it shows path and FSRef
variants of the API and how to get status out of the callbacks. The
API is conceptually similar to the FSVolumeOperation APIs introduced
in Mac OS X 10.2.
Example of FSCopyObjectAsync:
#import <Cocoa/Cocoa.h>
#interface AsyncCopyController : NSObject {
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
//delegate method
-(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
-(void)didCopyOperationComplete : (BOOL)boolean;
-(void)didReceiveCopyError : (NSString *)Error;
-(void)cancelAllAsyncCopyOperation;
#end
#import "AsyncCopyController.h"
static Boolean copying= YES;
#implementation AsyncCopyController
static void statusCallback (FSFileOperationRef fileOp,
const FSRef *currentItem,
FSFileOperationStage stage,
OSStatus error,
CFDictionaryRef statusDictionary,
void *info )
{
NSLog(#"Callback got called. %ld", error);
id delegate;
if (info)
delegate = (id)info;
if (error!=0) {
if (error==-48) {
[delegate didReceiveCopyError:#"Duplicate filename and version or Destination file already exists or File found instead of folder"];
}
}
CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
NSString* currentPath = [(NSURL *)theURL path];
// NSLog(#"currentPath %#", currentPath);
// If the status dictionary is valid, we can grab the current values to
// display status changes, or in our case to update the progress indicator.
if (statusDictionary)
{
CFNumberRef bytesCompleted;
bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
kFSOperationBytesCompleteKey);
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberMaxType,
&floatBytesCompleted);
// NSLog(#"Copied %d bytes so far.",
// (unsigned long long)floatBytesCompleted);
if (info)
[delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
}
NSLog(#"stage %d", stage);
if (stage == kFSOperationStageComplete) {
NSLog(#"Finished copying the file");
if (info)
[delegate didCopyOperationComplete:YES];
// Would like to call a Cocoa Method here...
}
if (!copying) {
FSFileOperationCancel(fileOp);
}
}
-(void)cancelAllAsyncCopyOperation
{
copying = NO;
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
{
NSLog(#"copySource");
copying = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(#"%#", runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp, FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop, kCFRunLoopDefaultMode);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
return status;
}
require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
//return NO;
}
// Create a filesystem ref structure for the source and destination and
// populate them with their respective paths from our NSTextFields.
FSRef source;
FSRef destination;
// Used FSPathMakeRefWithOptions instead of FSPathMakeRef
// because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference
status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&source,
NULL);
require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
Boolean isDir = true;
status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&destination,
&isDir);
require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)
FSFileOperationClientContext clientContext;
// The FSFileOperation will copy the data from the passed in clientContext so using
// a stack based record that goes out of scope during the operation is fine.
if (object)
{
clientContext.version = 0;
clientContext.info = (void *) object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}
// Start the async copy.
status = FSCopyObjectAsync (fileOp,
&source,
&destination, // Full path to destination dir
NULL,// Use the same filename as source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object != NULL ? &clientContext : NULL);
//CFRelease(fileOp);
NSLog(#"Failed to begin asynchronous object copy: %d", status);
if (status) {
NSString * errMsg = [NSString stringWithFormat:#" - %#", status];
NSLog(#"Failed to begin asynchronous object copy: %d", status);
}
if (object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopFailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;
}
#end
FSCopyObjectAsync is Deprecated in OS X v10.8
copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.