Ok, I'm trying to avoid global variables, so I read up on singleton classes.
This is a try to set and read a mutable array, but the result is null.
//Content.h
#interface Content : NSObject {
NSMutableArray *contentArray;
}
+ (Content *) sharedInstance;
- (NSMutableArray *) getArray;
- (void) addArray:(NSMutableArray *)mutableArray;
#end
.
//Content.m
#implementation Content
static Content *_sharedInstance;
+ (Content *) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[Content alloc] init];
}
return _sharedInstance;
}
- (NSMutableArray *) getArray{
return contentArray;
}
- (void) addArray:(NSMutableArray *)mutableArray{
[contentArray addObject:mutableArray];
}
#end
And in a ViewController I added #import "Content.h", where I try to call this:
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:#"test",#"foo",#"bar",nil];
Content *content = [Content sharedInstance];
[content addArray:mArray];
NSLog(#"contentArray: %#", [content getArray]);
You need to alloc and init the array first. Personally I'd do it in the init method of the content class like so:
-(id)init{
if(self = [super init]){
…the rest of your init code…
contentArray = [[NSMutableArray alloc] init];
}
return self;
}
You never actually alloc/initialise the contentArray array.
Related
I have two different view controllers ViewController.m & teacherList.m. I am trying to call the -(NSMutableArray *)getTeachersList method from teacherList.m to have it create an array for me and then be able to use it in ViewController.m. I want to do this just hard coding it and not with sequencing or use of storyboard
ViewController.m
#import "ViewController.h"
#import "teacherList.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize userView;
- (void)viewDidLoad
{
NSMutableArray *hey = [teacherList getTeachersList];;
NSString *hello = [hey objectAtIndex:0];
[self say:hello];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)say:(NSString *)greet{
userView.text = greet;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
teacherList.m File
#import "teacherList.h"
#interface teacherList ()
#end
#implementation teacherList
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSMutableArray *)getTeachersList{
NSURL *website = [NSURL URLWithString:#"http://www2.qcc.mass.edu/facAbsence/default.asp"];
NSURLRequest *request = [NSURLRequest requestWithURL:website];
NSURLResponse* response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//storing data in a string
NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//putting it into an array to be
//able to working wiht string or array
NSArray *newString = [myString componentsSeparatedByString:#"\n"];
NSMutableArray *teacherArray = [[NSMutableArray alloc]init];
NSMutableString *curLineNum;
NSString *curLine;
int i;
for (i = 0; i <[newString count]; i++) {
curLine = [newString objectAtIndex:i];
if ([curLine rangeOfString:#"<strong>"].location != NSNotFound) {
NSScanner *theScanner = [NSScanner scannerWithString:curLine];
[theScanner scanUpToString:#"<strong>" intoString:NULL];
[theScanner setScanLocation: [theScanner scanLocation]+8];
[theScanner scanUpToString:#"</strong>" intoString:&curLineNum];
[teacherArray addObject:curLineNum];
}
}
return teacherArray;
}
#end
Don't put the method getTeachersList in a view controller. Create a singleton object that is a subclass of NSObject. Let's call it TeacherListManager.
Then ask the singleton for the teacher's list.
The call might look like this:
TeacherListManager *theTeacherListManager = [TeacherListManager sharedTeacherListManager];
NSMutableArray *theTeacherList = theTeacherListManager.teachersList;
if (theTeacherList == nil)
//Handle the case where the teacher list hasn't been loaded yet.
Do a search on Objective C singleton design pattern for more information.
Your getTeachersList method is currently written to get the teachers list using synchronous networking calls, which is bad. If anything slows down the network connection then it can hang the UI for up to 2 minutes until the connection times out.
you should rewrite that method to download the data asynchronously.
My simplified singleton looks like this:
#interface MyClass : NSObject {
NSMutableArray * myArray;
}
+ (MyClass*) instance;
#property(nonatomic,retain)NSMutableArray *myArray;
Then in the implementation
static MyClass * myinstance;
#synthesize myArray;
+ (MyClass*) instance {
if(myinstance == nil)
myinstance = [[MyClass alloc] init];
return myinstance;
}
- (id) init {
if(self = [super init]) {
myArray = [[NSMutableArray alloc] initWithCapacity:2];
[myArray addObject:#"Trauma"];
}
return self;
}
However when I try to access it for a tableview this always returns 0:
[[[MyClass instance] myArray] count];
Little unsure about what I'm doing wrong
The method that returns the instance of your singleton is named +instance, but when attempting to access your singleton, you are using +myinstance. Your instance of MyClass is most likely nil.
I hope to add objects to a NSMutableArray "myArray", The NSMutableArray is the array for FileObj which has a NSString property "fileName"
#import <UIKit/UIKit.h>
#interface FileObj : NSObject {
NSString *fileName;
}
-(void) setfileName:(NSString *)s ;
-(NSString *) getfileName ;
#end
//
// File.m//
#import "File.h"
#implementation FileObj
-(void) setfileName:(NSString *)s ;
{
fileName=s;
}
-(NSString *) getfileName ;
{
return fileName;
}
#end
I initialize the myArray here:
NSMutableArray *temarray;
temarray=[[NSMutableArray alloc] init];
self.myArray=temarray;
[temarray release];
the codes to add object to myArray
FileObj *newobj=[[FileObj alloc]init ];
NSString *fieldValue2 = [[NSString alloc] initWithUTF8String:#"aaaa"];
[newobj setfileName:fieldValue2];
[myArray addObject:newobj];
[fieldValue2 release]; //**if I enabled the line, it will cause crash**
//**if I disable the line, it will cause memory leak**
[newobj release];
Welcome any comment
Thanks
interdev
First you should look into ObjC naming conventions. There is no -get methods in ObjC. It's also a good idea to prefix your classes with your own 2 letters (like NS).
Your setter value assignment is invalid and the NSString initialization unnecessary.
I would strongly recommend introductory material to you!
#interface MYFileObject : NSObject {
NSString *_fileName;
}
- (void)setFileName:(NSString *)theString;
- (NSString *)fileName;
#end
and the implementation
#implementation MYFileObject
- (void)setFileName:(NSString *)theString {
[_fileName release];
_fileName = [theString copy];
}
- (NSString *)fileName {
return [[_fileName copy] autorelease];
}
- (void)dealloc {
[_fileName release];
[super dealloc];
}
#end
You would add an object like this...
NSMutableArray *myAry = [[NSMutableArray alloc] init];
MYFileObject *obj = [[MYFileObject alloc] init];
[obj setFileName:#"thefilename.txt"];
[myAry addObject:obj];
[obj release];
I would recommend using properties instead of defining your own getters/setters.
You could also use the NSMutableArrays' designated initializers for fast array creation.
Look here for how to use properties: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
Why bother with getters and setters? Use declared property already!
#interface FileObj : NSObject {
NSString *fileName;
}
#property(retain,nonatomic) NSString* fileName; // <---
#end
...
#implementation FileObj
#synthesize fileName; /// <---
-(void)dealloc {
[fileName release]; // Remember to release the object on dealloc.
[super dealloc];
}
#end
...
FileObj *newobj=[[FileObj alloc] init];
NSString *fieldValue2 = [[NSString alloc] initWithUTF8String:#"aaaa"];
newobj.fileName = fieldValue2; /// <----
[myArray addObject:newobj];
[fieldValue2 release];
[newobj release];
The crash occurs because the NSString instance is not retained anymore.
A common pattern is to retain NSString properties, either declaratively with #property or by hand.
You should modify the setter like this:
-(void) setfileName:(NSString *)s ;
{
[s retain]; // <- Retain new value
[filename release]; // <- Release old value
fileName=s;
}
How can I create a class with the initializer initWithObjects?
Or does it just make more sense to inherit from NSArray and work around it that way?
initWithObjects: is implemented using a C variable argument list. Here's an example implementation:
- (void)setContentByAppendingStrings:(NSString *)firstArg, ...
{
NSMutableString *newContentString = [NSMutableString string];
va_list args;
va_start(args, firstArg);
for (NSString *arg = firstArg; arg != nil; arg = va_arg(args, NSString*))
{
[newContentString appendString:arg];
}
va_end(args);
[contents autorelease];
contents = [newContentString retain];
}
See this page for more info.
#interface foo : NSObject {
NSArray* objects;
}
-(id)initWithObjects:(NSArray*)array;
#end
#implementation foo
-(id)initWithObjects:(NSArray*)array{
if(self = [super init]){
objects = array;
}
return self;
}
#end
I have a feeling that this is stupid question, but I'll ask anyway...
I have a collection of NSDictionary objects whose key/value pairs correspond to a custom class I've created, call it MyClass. Is there an easy or "best practice" method for me to basically do something like MyClass * instance = [map NSDictionary properties to MyClass ];? I have a feeling I need to do something with NSCoding or NSKeyedUnarchiver, but rather than stumble through it on my own, I figure someone out there might be able to point me in the right direction.
The -setValuesForKeysWithDictionary: method, along with -dictionaryWithValuesForKeys:, is what you want to use.
Example:
// In your custom class
+ (id)customClassWithProperties:(NSDictionary *)properties {
return [[[self alloc] initWithProperties:properties] autorelease];
}
- (id)initWithProperties:(NSDictionary *)properties {
if (self = [self init]) {
[self setValuesForKeysWithDictionary:properties];
}
return self;
}
// ...and to easily derive the dictionary
NSDictionary *properties = [anObject dictionaryWithValuesForKeys:[anObject allKeys]];
There is no allKeys on NSObject. You'll need to create an extra category on NSObject like below:
NSObject+PropertyArray.h
#interface NSObject (PropertyArray)
- (NSArray *) allKeys;
#end
NSObject+PropertyArray.m
#import <objc/runtime.h>
#implementation NSObject (PropertyArray)
- (NSArray *) allKeys {
Class clazz = [self class];
u_int count;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++) {
const char* propertyName = property_getName(properties[i]);
[propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
free(properties);
return [NSArray arrayWithArray:propertyArray];
}
#end
Example:
#import "NSObject+PropertyArray.h"
...
MyObject *obj = [[MyObject alloc] init];
obj.a = #"Hello A"; //setting some values to attributes
obj.b = #"Hello B";
//dictionaryWithValuesForKeys requires keys in NSArray. You can now
//construct such NSArray using `allKeys` from NSObject(PropertyArray) category
NSDictionary *objDict = [obj dictionaryWithValuesForKeys:[obj allKeys]];
//Resurrect MyObject from NSDictionary using setValuesForKeysWithDictionary
MyObject *objResur = [[MyObject alloc] init];
[objResur setValuesForKeysWithDictionary:objDict];
Assuming that your class conforms to the Key-Value Coding protocol, you could use the following: (defined as a category on NSDictionary for convenience):
// myNSDictionaryCategory.h:
#interface NSDictionary (myCategory)
- (void)mapPropertiesToObject:(id)instance
#end
// myNSDictionaryCategory.m:
- (void)mapPropertiesToObject:(id)instance
{
for (NSString * propertyKey in [self allKeys])
{
[instance setValue:[self objectForKey:propertyKey]
forKey:propertyKey];
}
}
And here's how you would use it:
#import "myNSDictionaryCategory.h"
//...
[someDictionary mapPropertiesToObject:someObject];
If your doing this sort of thing chances are your dealing with JSON and you should probably have a look at Mantle
https://github.com/Mantle/Mantle
You will then get a convenient method dictionaryValue
[anObject dictionaryValue];
Just add category for NSObject for getting dictionaryRepresentation from your custom objects (in my case using in JSON serialization only):
// NSObject+JSONSerialize.h
#import <Foundation/Foundation.h>
#interface NSObject(JSONSerialize)
- (NSDictionary *)dictionaryRepresentation;
#end
// NSObject+JSONSerialize.m
#import "NSObject+JSONSerialize.h"
#import <objc/runtime.h>
#implementation NSObject(JSONSerialize)
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
return [[self alloc] initWithDictionary:aDictionary];
}
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
aDictionary = [aDictionary clean];
self.isReady = NO;
for (NSString* propName in [self allPropertyNames]) {
[self setValue:aDictionary[propName] forKey:propName];
}
//You can add there some custom properties with wrong names like "id"
//[self setValue:aDictionary[#"id"] forKeyPath:#"objectID"];
self.isReady = YES;
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSArray *propertyNames = [self allPropertyNames];
id object;
for (NSString *key in propertyNames) {
object = [self valueForKey:key];
if (object) {
[result setObject:object forKey:key];
}
}
return result;
}
- (NSArray *)allPropertyNames {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *rv = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[rv addObject:name];
}
//You can add there some custom properties with wrong names like "id"
//[rv addObject:#"objectID"];
//Example use inside initWithDictionary:
//[self setValue:aDictionary[#"id"] forKeyPath:#"objectID"];
free(properties);
return rv;
}
#end
Also, you can see that my solution will not work with custom objects with nested objects or arrays. For Arrays - just change the lines of code in dictionaryRepresentation method:
if (object) {
if ([object isKindOfClass:[NSArray class]]) {
#autoreleasepool {
NSMutableArray *array = [NSMutableArray array];
for (id item in (NSArray *)object) {
[array addObject:[item dictionaryRepresentation]];
}
[result setObject:array forKey:key];
}
} else {
[result setObject:object forKey:key];
}
}