So here is a partial sample of the relevant code.
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
- (IBAction)lookForRadioButtons:(id)sender {
// NSLog(#"Testing");
NSError *error;
NSString *radiostr = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"getRadios" ofType:#"txt"] encoding:NSASCIIStringEncoding error: &error] ;
if (radiostr == nil)
{
NSLog (#"Error! %#", error);
}
else
{
NSLog(#"%#",radiostr);
NSString *radiotxt= [webView stringByEvaluatingJavaScriptFromString:radiostr];
NSLog(#"%#", radiotxt);
NSArray *myRadios = [radiotxt componentsSeparatedByString:#"::"];
[radioInputArray addObjectsFromArray:myRadios];
NSLog(#"%d", myRadios.count);
NSLog(#"Number of buttons in global radio array %d", radioInputArray.count);
NSLog(#"%d", scrollViewer.subviews.count);
}
}
So it throws no exceptions and seems to work properly except after addObjectsFromArray:, my count in the global NSMutableArray is 0 (the count in the myRadios = 56). I am pretty sure they should be equal at this point but are not. I have declared my NSMutableArray up near the top so that it can be globally accessed. Am I missing something such as allocating and initializing this? Does it not do that automatically like in C#? Again, this is my first foray into the Objective-C world from Windows programming so please be gentle yet feel free to be critical.
Your two global arrays are not initialized.
The lines
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
just define the two variables as pointers to NSMutableArray, so you need to get them to point at an actual instance of the class NSMutableArray.
Somewhere in your initialization code, or through an accessor (best if a class method), you should set the variables to an empty, newly allocated NSMutableArray.
Here is a way to do it:
+ (NSMutableArray*)radioInputArray
{
if (!radioInputArray) {
radioInputArray = [[NSMutableArray alloc] init];
}
return radioInputArray;
}
Then use the accessor in your code instead of the global variable.
It may happen if your radioInputArray is nil,
you didn't initialize the array
you need to add
[[radioInputArray alloc] init];
before you do anything with radioInputArray
Good place for initialising object is "init" method in Global class
Ex.
-(id)init
{
if (self=[super init]) {
self.globalAllArtworkArray=[[NSMutableArray alloc] init];
self.globalCollectionArray=[[NSMutableArray alloc] init];
self.globalLookbookArray=[[NSMutableArray alloc] init];
}
return self;
}
+(ASNGlobalClass *)shareManager
{
static ASNGlobalClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Related
I am relatively new to GCD, I am currently using it to create a singleton object in my application. After doing some research I found using GCD's dispatch_once() method is the best way to achieve the singleton design pattern. For some reason, my code is breaking and I can not figure it out for the life of me. Below, I have pasted my singleton creation/init code, and the responding error.
+(id)sharedErrorMapper {
static dispatch_once_t onceToken;
static id sharedInstance;
dispatch_once(&onceToken, ^
{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(id)init {
//creating the ErrorMap data structure
//currently loading in dummy JSON data..
self = [super init];
if (self != nil) {
NSError *error = [[NSError alloc] init];
NSData *resourceData = [[NSData alloc] initWithContentsOfFile:
#"/Users/acuram/Desktop/GitHubWorkspaces/ios-sdk-src/ios-here-sdk/PPHSDKTests/Resources"];
self.errorMap = [[NSDictionary alloc] init];
self.errorMap = [NSJSONSerialization JSONObjectWithData:resourceData options:kNilOptions error:&error];
}
return self;
}
After setting some breakpoints and doing some stack tracing, my code is breaking at the dispatch_once() function call. The error I get back is a "NSInvalidArgumentException", it complains that my data parameter is nil. I am pretty shocked because I followed a legit tutorial video on youtube to implement this, I am also looking at my companies code base and they seem to do it in a similar way....
Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?
That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)
I'm totally stumped on this one. I have an NSMutableArray which is declared in my header and set as a property, synthesized etc. I then call a function that allocates and initializes the array, and I add custom objects to it. I do a for each loop after the objects are added to ensure that they are actually contained within the array and they are. Once the program goes outside of this function scope, though, suddenly the array is empty.
header file:
#interface ScheduleViewController : UITableViewController {
NSString *login_id;
NSMutableArray *events;
}
- (id)initWithID:(NSString*)l_id;
- (void)grabURLInBackground; // ASIHTTP example method
#property (nonatomic, retain) NSString *login_id;
#property (nonatomic, retain) NSMutableArray *events;
#end
implementation:
#synthesize events;
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Allocate empty event object and initialize the mutable array
Event* event = [[Event alloc] init];
self.events = [[NSMutableArray alloc] initWithCapacity:[eventDics count]];
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
So the above code works fine and prints out the variables from the Event objects that are stored in the events mutable array.
What I want to do is use the events array in another function now, and when I try to, the count is 0 and also no objects are stored in the array when I look at it.
In viewDidUnload I set self.events = nil; and in dealloc I do [self.events release]
You are doing your alloc/init for the Event *event object outside of your for loop. This means you are adding the same object every time you add it to the array. You should move this line:
Event* event = [[Event alloc] init];
To the inside of your
for (int i=0 ... loop.
Not sure that would explain the symptoms you are seeing, but it could, since the following statement:
[event release]
is also releasing that one allocated object once for every time through the loop - so you are releasing the object multiple times. If you move the Event alloc to the inside of the loop then the release will be ok. (adding the object to the array will retain it so its ok to release it, but you need to allocate a new Event each time through the loop).
Basically your code should look like this: (note I've also added an autorelease to your array alloc).
// Allocate empty event object and initialize the mutable array
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease]; // assigning to the retain property will retain it, so autorelease it or it will be retained twice. Could also have used the arrayWithCapacity convenience method here instead and then wouldn't need to autorelease.
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
Event* event = [[Event alloc] init]; // Allocate a new Event each time through the loop so you are adding a unique object to the array each time.
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
I see many problems with this code. These include the fact that you are releasing objects at inappropriate times, and that you are getting confused about the scope of different objects. It seems to me that one of the biggest problems that you are having is re-allocating your events array every time the requestFinished: method is called. In your init method, you should do something like this:
- (id)init {
if ((self = [super init])) {
// Since it is a retain property, we should autorelease it when
// assigning to it, thus preventing an extra retain.
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease];
}
}
With that being said, here is how I would rewrite your requestFinished: method, as well as your dealloc method:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Clear the already allocated events array
[self.events removeAllObjects];
for (int i = 0; i < [eventDics count]; i++) {
// note how I assign event in here
Event *event = [[Event alloc] init];
NSDictionary *dict = [eventDics objectAtIndex:i];
for (NSString *key in dict) {
// Do whatever it is you do here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
Finally, you can simply set the events property to nil in the dealloc method:
- (void)dealloc {
self.events = nil;
[super dealloc];
}
The only reason that I can think of for the array being empty is that a) it's contents are being deallocated, or b) it itself is being deallocated and set to nil. The pieces of your code that I fixed could possibly cause both of these. Try the changes that I have made, and see if they make a difference.
So I figured out the problem and it's due to an error on my part. After stepping through the function calls more closely, it turns out that the table view delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
was being called before populating the array with requestHasFinished. I was calling requestHasFinished indirectly through viewDidLoad but I think that the table view delegate method was being called when the view controller is initialized. Init was being called before viewDidLoad because the view controller is actually handled within a tab view controller which initializes all of the view controllers for each tab at the time when itself is initialized. That's another matter to get into.. I'm not sure if I like everything being initialized and setup before the views are even displayed but.. something to research.
Anyways thanks again for the help.
I am relatively new to Objective-C and now I have a problem in my iPhone app that I don't fully understand.
I try to use a NSMutableDictionary, this does not seem to work as i expect for some reason. When I run the debugger and do po numberToCallerMap to see the dictionary, I get an exception. I have read the documentation for NSMutableDictionary on how to initialize it, but I can not see what I am doing wrong. Help and advice are appreciated. The variable causing me problem is numberToCallerMap, here is the relevant function:
- (void)setData:(NSString*)value{
[list release];
list = [[NSMutableArray alloc] init];
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSMutableDictionary* numberToCallerMap;
CallerInfo* caller;
NSDictionary* callerInfo;
#try {
NSArray *array = (NSArray*)[json objectWithString:value];
// reading all the items in the array one by one
numberToCallerMap = [NSMutableDictionary dictionary];
for (id *item in array) {
// if the item is NSDictionary (in this case ... different json file will probably have a different class)
NSDictionary *dict2 = (NSDictionary *) item;
CallInfo *data = [CallInfo alloc];
[data initFromDictionary:dict2];
callerInfo = (NSDictionary*)[dict2 valueForKey:#"caller"] ;
//Here, we want the phonenumber to be part of the CallerInfo object instead.
// It is sent from the server as part of the Call-object
NSString* number = (NSString*)[dict2 valueForKey:#"phoneNumber"];
[callerInfo setValue:number forKey:#"phoneNumber"];
caller = (CallerInfo*)[numberToCallerMap valueForKey:number];
if(caller == nil || [caller isKindOfClass:[NSNull class]]){
caller = [CallerInfo alloc];
[caller initFromDictionary:callerInfo];
[numberToCallerMap setValue:caller forKey:number];
[list insertObject:caller atIndex:0];
}
[caller addRecentCall:data];
}
}
#catch (NSException * e) {
[list release];
list = [[NSMutableArray alloc] init];
}
#finally {
[numberToCallerMap release];
}
}
This is probably not the only problem, but you are not alloc-ing your numberToCallerMap dictionary, you are getting it from a convenience class method -- [NSMutableDictionary dictionary] -- that returns it autoreleased. So you should not call release on it yourself.
I was trying to create a static variable to store a dictionary of images. Unfortunately, the best way I could find to initialise it was to check in each function that used the variable. Since I am creating this variable inside a category, I can't just initialise it inside the initialiser. Is there a neater way of initialising navigationBarImages?
static NSMutableDictionary *navigationBarImages = NULL;
#implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
- (void)drawRect:(CGRect)rect
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
NSString *imageName=[navigationBarImages objectForKey:self];
if (imageName==nil) {
imageName=#"header_bg.png";
}
UIImage *image = [UIImage imageNamed: imageName];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
[navigationBarImages setObject:image forKey:self];
}
#end
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
These function will be called automatically when the program starts and ends.
Consider this approach,
static NSMutableDictionary *navigationBarImages()
{
static NSMutableDictionary *dict = NULL;
if(dict == NULL)
{
dict = [[NSMutableDictionary alloc] init];
}
return [[dict retain] autorelease];
}
then whenever you woulde use navigationBarImages, replace it with navigationBarImages(), like this:
change
NSString *imageName=[navigationBarImages objectForKey:self];
to
NSString *imageName=[navigationBarImages() objectForKey:self];
If the function call overhead bothers you, maybe use a temporary variable to catch the return of navigationBarImages(),
NSMutableDictionary *dict = navigationBarImages();
[dict doSomething];
[dict doSomething];
The drawback is once you called navigationBarImages(), the instance of NSMutableDictionary got created, then it'll never get chance to dealloc until the end of the program.
All you need is to set your static once at a known point before it is used. For example, you can set an NSApplication delegate and have it do the work in -applicationDidFinishLaunching:
One option is to use C++. Change the file's extension to .mm and replace = NULL with [[NSMutableDictionary alloc] init].
You could add +initialize in the .m file of your category — you'll just need to make sure you're not smashing an existing implementation or you'll get general wonkiness. (Obviously, you can be sure of this if you wrote the code, but with third-party code, this is probably not the best approach.)