How to handle NSData as a #property - objective-c

I'm trying to get a #property to work with NSData so that it becomes available to all methods in the class.
I've set up a little test project to help me debug the problem.
In my main.m is
FxPlug *data = [[FxPlug alloc] init];
[FxPlug makeCube];
NSLog (#"%#",[FxPlug.cubeData]);
and in the Class FxPlug.h is
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
#property NSData *cubeData;
+(void) makeCube;
#end
and FxPlug.m is
#import "FxPlug.h"
#implementation FxPlug
+ (void) makeCube; {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
NSData *cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
So this is just a test to see if I can set the #property NSData *cubeData; in class FxPlug to the contents of a file and to see if it is passed back into the main.m class to NSLog out to the terminal.
I'd be really really appreciative of some help.
Basically, I'm trying to pass an NSData object between classes.
Many thanks!
Because my main project is an FxPlug, which is basically just one big class with a load of methods that are named specifically for the host app. So I need to add a new class method to that existing class to handle the NSData object. Or at least that's how I think it works!

If you want to use a property then you have to use and create a class instance and avoid using class methods, so:
FxPlug.h:
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
#property NSData *cubeData;
-(void) makeCube;
#end
FxPlug.m:
#import "FxPlug.h"
#implementation FxPlug
- (void) makeCube {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
self.cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
main.m:
FxPlug *data = [[FxPlug alloc] init];
[data makeCube];
NSLog (#"%#", data.cubeData);
It can be done with class methods, and class-level variables, but not with #propertys. One halfway house is to use the singleton pattern where you only allow one instance of the class to exist at any one time.
EDIT: If you want to do it as a static class (not recommended), the code is below:
FxPlug.h:
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
+ (NSData *)cubeData;
+ (void)setCubeData:(NSData *)cubeData;
+ (void) makeCube;
#end
FxPlug.m:
#import "FxPlug.h"
static NSData *_cubeData = nil;
#implementation FxPlug
+ (NSData *)cubeData
{
return _cubeData;
}
+ (void)setCubeData:(NSData *)cubeData
{
_cubeData = cubeData;
}
+ (void) makeCube {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
_cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
main.m:
[FxPlug makeCube];
NSLog (#"%#", [FxPlug cubeData]);

Related

Initialising CoreData in AppleScript-objc

I'm trying to write and read from CoreData on OSX, combinig cocoa classes with applescript-objc.
I wrote methods to handle my CoreData as in tutorial (youtube, Cocoa Tutorial: Core Data Introduction in iOS and Mac OS Programming Part 2) <- those are cocoa classes in my project
I have a class called CoreDataHelper that defines methods to manipulate Core Data. Here are some of them:
+(NSManagedObjectContext *) managedObjectContext{
NSError*error;
[[NSFileManager defaultManager] createDirectoryAtPath:[CoreDataHelper directoryForDatabaseFilename] withIntermediateDirectories:YES attributes:nil error:&error];
if(error){
NSLog(#"%#", [error localizedDescription]);
return nil;
}
NSString* path = [NSString stringWithFormat:#"%#/%#",[CoreDataHelper directoryForDatabaseFilename],[CoreDataHelper databaseFilename]];
NSURL *url = [NSURL fileURLWithPath:path];
NSManagedObjectModel* managedModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator* storeCordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedModel];
if(![storeCordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]){
NSLog(#"%#",[error localizedDescription]);
return nil;
}
NSManagedObjectContext* managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
return managedObjectContext;
}
+(id)insertManagedObjectOfClass:(Class) aClass inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext{
NSManagedObject* managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
return managedObject;
}
I'm using .xcdatamodeld for my Model.
I have 2 entities in it with classes for each one of them:
Tab
Service
Tab.h looks like this
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Service;
NS_ASSUME_NONNULL_BEGIN
#interface Tab : NSManagedObject
// Insert code here to declare functionality of your managed object subclass
#end
NS_ASSUME_NONNULL_END
#import "Tab+CoreDataProperties.h"
and Tab.m looks like this:
#import "Tab.h"
#import "Service.h"
#implementation Tab
// Insert code here to add functionality to your managed object subclass
#end
Tab+CoreDataProperties.h:
#import "Tab.h"
NS_ASSUME_NONNULL_BEGIN
#interface Tab (CoreDataProperties)
#property (nullable, nonatomic, retain) NSString *nZakladka;
#property (nullable, nonatomic, retain) NSString *uZakladka;
#property (nullable, nonatomic, retain) Service *podstronaSerwisu;
#end
NS_ASSUME_NONNULL_END
Tab+CoreDataProperties.m:
#import "Tab+CoreDataProperties.h"
#implementation Tab (CoreDataProperties)
#dynamic nZakladka;
#dynamic uZakladka;
#dynamic podstronaSerwisu;
#end
Now, my AppDelegate.applescript looks like this
...
property CoreDataHelper: class "CoreDataHelper"
property cService: class "Service"
property cTab: class "Tab"
script AppDelegate
on applicationWillFinishLaunching_(aNotification)
set theContext to CoreDataHelper's managedObjectContext()
set ccTab to cTab's alloc()'s init()
//the one below cause an error "CoreData: error: Failed to call designated initializer on NSManagedObject class 'Tab'"
set theTab to CoreDataHelper's insertManagedObjectOfClass_inManagedObjectContext_(ccTab, theContext)
end applicationWillFinishLaunching_
...
end Script
When I run whole thing I got an error message posted in a commented line in AppleScriptObjC code. The method that is causing an error is NSManagedObject* managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
What is the problem?

Object copying in Objective-C raises the error : unrecognized selector sent to instance

I'm playing around objects copying in Objective-C and I got the infamous unrecognized selector sent to instance.
I created the Person class like this :
#import <Foundation/Foundation.h>
#interface Person : NSObject{
NSString *firstName;
NSString *familyName;
}
#property(nonatomic, copy) NSString *firstName;
#property(nonatomic, copy) NSString *familyName;
#end
and implemented :
#import "Person.h"
#implementation Person
#synthesize firstName, familyName;
-(NSString *)description{
return [NSString stringWithFormat:#"My name is %# %#", firstName, familyName];
}
#end
Then I use it in the main function :
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Person *a = [[Person alloc] init];
[a setFirstName:#"Foo"];
[a setFamilyName:#"Bar"];
NSLog(#"%#", a);
Person *b = [a copy];
NSLog(#"%#", b);
}
return 0;
}
The error raises when the instruction [a copy] is being executed.
Why this error ? My object inherits from the NSObject so, I thought it would call the following method :
-(id)copy{
return [self copyWithZone:NULL]:
}
So why it doesn't work ? Can we say that object copying using the copy method doesn't work ?
Regards,
You have to add NSCopying protocol to your custom class and you need override copyWithZone: method:
- (id)copyWithZone:(NSZone *)zone
{
// Copying code here.
}
After that you can call copy on your object.

Still confused by Objective C variable scope

I am trying to get some employee data from a JSON service. I am able to get the data and load it into an NSMutableArray, but I cannot access that array outside the scope of the method that gets the data.
TableViewController h filed
#import <UIKit/UIKit.h>
#import "employee.h"
#interface ViewController : UITableViewController
{
//NSString *test;
//NSMutableArray *employees;
}
#end
And here is my m file:
#define kBgQueue dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define scoularDirectoryURL [NSURL URLWithString: #"https://xxxx"]
#import "ViewController.h"
#interface ViewController()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
scoularDirectoryURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSMutableArray *jsonArray = [NSJSONSerialization JSONObjectWithData: responseData options: NSJSONReadingMutableContainers error: &error];
id jsonObject = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSMutableArray *employees = [[NSMutableArray alloc ]init];
if (!jsonArray) {
} else {
for (jsonObject in jsonArray){
employee *thisEmployee = [employee new];
thisEmployee.fullName = [jsonObject objectForKey:#"$13"];
thisEmployee.state = [jsonObject objectForKey:#"state"];
thisEmployee.city = [jsonObject objectForKey:#"city"];
[employees addObject:thisEmployee];
}
}
}
Any help would be appreciated.
Bryan
You were on the right track. All you have to do is uncomment the NSMutableArray declaration in your #interface, and then change this line:
NSMutableArray *employees = [[NSMutableArray alloc] init];
to this
employees = [[NSMutableArray alloc] init];
Declaring the array in your interface will allow it to be accessed from anywhere within your implementation, or even from other classes and files if you declare it as a public property. When you make a declaration inside a function, that variables scope does not extend to outside of the function.
Just to elaborate a little on the scope of the variables, you have several ways of declaring them. The most used are:
Instance variables, which are declared in your interface and they can be accessed from any method inside the class or inside any method from it's subclasses. For example:
#interface MyObject : NSObject { //this can be any class
NSString *instanceVariable;
}
#implementation MyObject
-(void)someStrangeMethod {
instanceVariable = #"I'm used here";
NSLog(#"%#",instanceVariable);
}
//from subclasses
#interface MySubclassObject: MyObject {
//see that the variable is not declared here;
}
#implementation MySubclassObject
-(void)anotherStrangeMethod {
[super someStrangeMethod]; // this will print the value "I'm used here"
instanceVariable = #"I'm changing my value here"; //here we access the variable;
}
If you want the instance variable to be accessed only from the "owner" class you can declare it after the #private tag. You also have the #protected tag, though that isn't used so much.
If you want to have a variable that can be accessed outside the class, declare it as a property in your interface.
Also you can make the properties private using #private but this will contradict the purpose of the properties.

How to redirect certain NSLog output to a file

I'm using the code to write all NSLogs to a text file. How can I select the NSlogs that I only need to write to file?
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *logPath = [documentsDirectory stringByAppendingPathComponent:#"MOVbandlog.txt"];
//freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
freopen([logPath fileSystemRepresentation],"a+",stderr);
You might want to take a look at Lumberjack, which is a comprehensive logging library.
Please have a look this may help :
Redirecting the nslog output to a file is useful in scenerios when device is not connected to system and we need the console logs to track down some issues.In order to do this we added one singelton class USTLogger subclass of NSObject :
Source code for USTLogger.h file are following :
#import <Foundation/Foundation.h>
#interface USTLogger : NSObject
{
BOOL stdErrRedirected;
}
#property (nonatomic, assign) BOOL stdErrRedirected;
-(void) writeNSLogToFile;
+ (USTLogger *) sharedInstance;
Source code for USTLogger.m file are following :
#import "USTLogger.h"
#import <unistd.h>
#implementation USTLogger
#synthesize stdErrRedirected;
static int savedStdErr = 0;
static USTLogger *sharedInstance;
+ (USTLogger *) sharedInstance {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(id)init {
return self;
}
- (void) writeNSLogToFile
{
if (!stdErrRedirected)
{
stdErrRedirected = YES;
savedStdErr = dup(STDERR_FILENO);
NSString *cachesDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *logPath = [cachesDirectory stringByAppendingPathComponent:#"nslog.log"];
freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
}
}
- (void)restoreStdErr
{
if (stdErrRedirected)
{
stdErrRedirected = NO;
fflush(stderr);
dup2(savedStdErr, STDERR_FILENO);
close(savedStdErr);
savedStdErr = 0;
}
}
After the implementation of USTLogger Class we just need to call these method when we need nslog in file
#import "AppDelegate.h"
#import "USTLogger.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[USTLogger sharedInstance] writeNSLogToFile];
NSLog(#"didFinishLaunchingWithOptions");
return YES;
}
#end
This will write whole nslog output in a file and it is stored in application document directory, we can get that nslog file by enabling file sharing for application.
source code be downloaded from below link :
https://shankertiwari3.wordpress.com/2015/06/09/redirect-the-nslog-output-to-file-instead-of-console/
You can create two version of NSLog, as MYLog using only NSLog, will show on console. Second one MYLogFile that will call NSLog and write to file as well.
So basically you need to make two macros or methods.

Why is this instance object being changed?

I'm very new at objective C, I'm just learning. I did the techotopia tutorial "An_Example_SQLite_based_iOS_4_iPhone_Application_(Xcode_4)", then tried to implement it again with FMDB. (I'd post the link to the tutorial but it let's me only post 2 links max)
The problem: In initWithFrame I create eventDB. Then in addEvent, after a keypress, the eventDB.database's contents are changed. This is eventDB in initWithFrame and this is it in addEvent.
#import "appTracker.h"
#implementation appTracker
- (id) initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
eventDB = [[appTrackerDB alloc] init];
return self;
}
- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == 'A') {
NSLog (#"Adding event");
[self addEvent:#"test_arg"];
}
}
- (void) addEvent: (NSString *) name
{
[eventDB setName:name];
[eventDB setPhone:name];
[eventDB setAddress:name];
[eventDB setStatus:name];
[eventDB saveData];
}
...
#end
Using GDB I stepped through and found that it is changing in main.m (autogenerated by XCode4) here: (not really sure what this code does or why it's there)
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
I'm unfamiliar with objective C. Can someone help me figure out why my eventDB.database object is being changed? I'm probably not managing some memory correctly or totally misinterpreting how you are supposed to do this. Any help would be appreciated.
eventDB is an instance of:
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#interface appTrackerDB : NSObject {
NSString *name;
NSString *address;
NSString *phone;
NSString *status;
NSString *databasePath;
FMDatabase *database;
}
Thanks!
Also [eventDB saveData] is:
- (void) saveData
{
[database executeUpdate:#"insert into user (name, address, phone) values(?,?,?)",
name, address, phone,nil];
}
And created the database with:
#implementation appTrackerDB
#synthesize name,address,status,phone;
- (id)init
{
self = [super init];
if (self) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"database.sqlite"];
database = [FMDatabase databaseWithPath:path];
[database open];
[database executeUpdate:#"create table IF NOT EXISTS user(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT, PHONE TEXT)"];
if ([database hadError]) {
NSLog(#"DB Error %d: %#", [database lastErrorCode], [database lastErrorMessage]);
}
name = #"TEST";
}
return self;
}
You don't actually retain the Database. In Objective-C you need to manually retain the Objects, especially if they are not properties. (e.g. name is declared as a property, database is not)
Retaining means that you own the Object. databaseWithObject retains the Database but calls autorelease on it, which normally means, it will delete the reference as soon as possible after the calling method is finished.
Depending on your platform, e.g. OS X instead of iOS, you could enable the GarbageCollection-feature. This would mean, that the OSX/Objective-C environment would do a lot of the memory management for you. But for this to be of any use, you would need to the declare the pertaining instance variables as properties and use the appropriate setter- and getter-methods on them.
Here is an example of a property-declaration (appTrackerDB.h):
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#interface appTrackerDB : NSObject {
/*
These are only necessary when
using iOS-Versions prior to
iOS 4, or if you really
need to manipulate the values
without utilizing the setter-/getter-
methods.
*/
NSString *name;
NSString *address;
NSString *phone;
NSString *status;
NSString *databasePath;
FMDatabase *database;
}
#property (retain) NSString *name,*address;
#property (retain) NSString *phone,*status,*databasePath;
#property (retain) FMDatabase *database;
appTrackerDB.m:
#implementation appTrackerDB
#synthesize name,address,status,phone;
#synthesize databasePath,database;
An example setter method you would call instead of you manual assignment is:
[self setDatabase:...]; instead of assigning a value directly database = ...
Setter methods like setVariableName and getter methods like variableName
are synthesized for you by the #synthesize directive.