I have a map view that instantiates a presentation model for running all it's logic in my application. I have a list component. User selects some data they want to see on the map. This gets sent to my model and once update is called on the view I pass an NSObject over to the Map view and then want to hand off this data to the PM in order to run logic on the view.
Here is my code for Map view. The very last line is where things crash "[pm showMKL:d];" gets called. If I comment out this line things run smooth.
This is an iOS 6 app so ARC is being used. Some searching suggested a memory leak but running the profiler didn't seem to suggest that. Also I was not able to produce the crash if I run the app with profiling on.
#import "MapView.h"
#implementation MapView
-(id)initWithModel:(MainDM*)dm:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_dm = dm;
[_dm register:self];
pm = [[MapPM alloc] initWithModel:_dm];
CLLocationCoordinate2D coord = {.latitude = 32.61724, .longitude = -106.74128};
MKCoordinateSpan span = {.latitudeDelta = 1, .longitudeDelta = 1};
MKCoordinateRegion region = {coord, span};
[self setRegion:region];
}
return self;
}
-(void)update:(DataObject*)data
{
if([[data getType] isEqualToString:#"CURRENT_KML_CHANGE"])
{
KmlVO *d = (KmlVO*)[data getData];
[pm showMKL:d];
}
}
#end
Here is the .h for MapView
#import <MapKit/MapKit.h>
#import "MainDM.h"
#import "BBView.h"
#import "MapPM.h"
#interface MapView : MKMapView <BBView>
#property(nonatomic,strong)MainDM *dm;
#property(nonatomic,strong)MapPM *pm;
-(void)update:(DataObject*)data;
-(id)initWithModel:(MainDM*)dm:(CGRect)frame;
#end
Here is the MapPM code.
#import "MapPM.h"
#implementation MapPM
-(id)initWithModel:(MainDM *)dm
{
self = [super init];
if(self)
{
_baseMaps = [[NSMutableArray alloc] init];
_dm = dm;
}
}
-(void)showMKL:(KmlVO*)vo
{
if([_baseMaps containsObject:vo])
{
NSLog(#"not added");
}
else
{
NSLog(#"added kml");
[_baseMaps addObject:vo];
}
}
#end
here is the .h for the PM
#import <Foundation/Foundation.h>
#import "MainDM.h"
#import "KmlVO.h"
#interface MapPM : NSObject
#property(nonatomic, retain)NSMutableArray * baseMaps;
#property(nonatomic, retain)MainDM *dm;
-(id)initWithModel:(MainDM *)dm;
-(void)showMKL:(KmlVO*)vo;
#end
I didn't need to see your header files after all.
-(id)initWithModel:(MainDM *)dm
{
self = [super init];
if(self)
{
_baseMaps = [[NSMutableArray alloc] init];
_dm = dm;
}
}
You're not returning self, and as such, you're performing showMKL on an undefined object. Did you check your warnings?
Also, you're using properties (not instance variables within #interface like you led us to believe). As such, [pm showMKL:data] should really be [self.pm showMKL:data].
Related
Another newbie question. I am trying to understand how to use scope effectively to organise my projects using classes to hold the data instead of having everything on the view controller. So, I am working on a couple of versions of a simple project to understand how scope works.
I have a view controller hooked to a view. In that view there are buttons that when clicked show images. I want to add another button that randomizes the images. I also have a class called "Cards" to hold the cards and the methods for creating and shuffling the cards. I have duplicated the project, so I have one that works and one that doesn't.
First project. These are the files:
view controller h file:
#import <UIKit/UIKit.h>
#import "Cards.h"
#interface ViewController : UIViewController
- (IBAction)buttonPressed:(id)sender;
#end
view contoller m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Cards *instance = [[Cards alloc] init];
instance.images = [instance createImages];
NSLog(#"I've got %lu Images", (unsigned long)instance.images.count);
instance.shuffled = [instance shuffleImages];
NSLog(#"Image numbers shuffled: %#", instance.shuffled);
}
- (IBAction)buttonPressed:(id)sender {
//Nothing hooked to this yet
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Cards h file:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface Cards : NSObject
// Creating Images
#property NSMutableArray *images;
- (NSMutableArray*) createImages;
//Shuffling Images
#property NSMutableArray *shuffled;
- (NSMutableArray*) shuffleImages;
#end
Cards m file:
#import "Cards.h"
#implementation Cards
- (NSMutableArray*) createImages{
self.images = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Image1.png"],
[UIImage imageNamed:#"Image2.png"],
[UIImage imageNamed:#"Image3.png"],
[UIImage imageNamed:#"Image4.png"], nil];
return self.images;
}
- (NSMutableArray*) shuffleImages{
NSUInteger imageCount = [self.images count];
NSMutableArray *localvar = [[NSMutableArray alloc]init];
for (int tileID = 0; tileID < imageCount; tileID++){
[localvar addObject:[NSNumber
numberWithInt:tileID]];
}
for (NSUInteger i = 0; i < imageCount; ++i) {
NSInteger nElements = imageCount - i;
NSInteger n = (arc4random() % nElements) + i;
[localvar exchangeObjectAtIndex:i
withObjectAtIndex:n];
}
return localvar;
}
#end
This works and I get the expected output on the console:
2015-12-31 23:43:44.885 VCScope[2138:533369] I've got 4 Images
2015-12-31 23:43:44.886 VCScope[2138:533369] Image numbers shuffled: (
0,
2,
3,
1
)
Second project:
What I want to do, is put a button to randomize the images only when the button is pressed and not as part of viewDidLoad. So, in my second project, I have the same files for the view controller.h and for both the Cards.h and Cards.m, but on the view controller.m I move the calling of the method for the shuffling of the cards to a UIButton method, like so:
new View controller m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Cards *instance = [[Cards alloc] init];
instance.images = [instance createImages];
NSLog(#"I've got %lu Images", (unsigned long)instance.images.count);
}
- (IBAction)buttonPressed:(id)sender {
Cards *instance = [[Cards alloc] init];
instance.shuffled = [instance shuffleImages];
NSLog(#"Image numbers shuffled: %#", instance.shuffled);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This outputs to the console the following:
2015-12-31 23:32:07.495 4StackVCScope[2029:486608] I've got 4 Images
2015-12-31 23:32:11.924 4StackVCScope[2029:486608] Image numbers: (
)
So it's not working and I am guessing it's to do with scope. Can someone throw some light into this? thanks
Welcome to Stack Overflow. You mention you're a "newbie", but it would be helpful to know what background you have so I know how much detail is needed here.
Cards *instance = [[Cards alloc] init];
creates a fresh Cards instance in a local variable. You are doing this separately inside -viewDidLoad and in -buttonPressed:.
If you want one Cards object per ViewController, then the view controller needs to have per-instance storage for it. There are several possible ways to do this. Which one you pick is a question of code style and API design.
If the Cards instance is for internal use only, you can declare an ivar in your #implementation block:
#implementation ViewController {
Cards *_cards;
}
- (void)viewDidLoad { _cards = ... }
- (IBAction)buttonPressed:(id)sender { access _cards }
#end
(Ivars can be declared in the public #interface as well, but I wouldn't recommend that as it leaks implementation details.)
Or you can use a property in the public interface:
// in your .h file:
#interface ViewController
#property (nonatomic) Cards *cards;
#end
// in your #implementation:
- (void)viewDidLoad { self.cards = ... }
- (IBAction)buttonPressed:(id)sender { access self.cards }
A property can also be privately declared in a class extension:
// in your .m file:
#interface ViewController ()
#property (nonatomic) Cards *cards;
#end
I have the IntroScene, and I wanna add a node, but it doesn't seem to work. Here are two different ways I tried doing it, and BOTH failed.
First way, failed:
in hearts2.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts2 : CCNode {
}
#end
in hearts2.m
#import "Hearts2.h"
#implementation Hearts2
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart2 *heart;
[self addChild:heart z:2];
// done
return self;
}
I didn't expect that to work (actually I was desperate and tried it that way as the second way just to see if it would work). The actual first attempt I tried to do was this, and it also Failed:
in hearts1.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts1 : CCNode
+ (Hearts1 *)node;
- (id)init;
-(void)selfAnimate;
#end
in hearts1.m
#import "Hearts1.h"
#implementation Hearts1 {
}
+ (Hearts1 *)node
{
return [[self alloc] init];
}
- (id)init
{
self = [super init];
if (!self) return(nil);
return self;
}
- (void)dealloc
{
}
- (void)onEnter
{
[super onEnter];
}
- (void)onExit
{
// always call super onExit last
[super onExit];
}
- (void)selfAnimate
{
}
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart1 *heart;
[self addChild:heart z:2];
// done
return self;
}
Please, I would do anything if someone could help me figure this out thanks everyone very much. I always get the SigABRT so I have no idea what is going wrong. I'm sure I'm just stupid and don't know how to code and missing something simple.
heart2 *heart;
You named your class Hearts2 so use the exact same name, including uppercase.
Secondly you created a variable but this will be nil. If you aren't using ARC (which you should) this will create an uninitialized object.
This will create an instance of Hearts2, assign it to the local var heart and add it as a child:
Hearts2 *heart = [Hearts2 node];
[self addChild:heart z:2];
I've asked questions on here so many times about this ruddy game that I'm trying to make. I'm working on a Text-Based adventure game. First I made it in Java because that's what I was learning the the class the game was for. Now I'm trying to learn iOS development which requires objective-c. I feel pretty comfortable with objective c after taking the Lynda Essentials course (The previous experience with Java helped of course). Anyways I'm working on this game and I'm running into a problem that seems pretty unique to objective c.
In Java when I have multiple classes they just need to be in the same directory in order for me to use them in other classes. This is not the case in Objective-C... I have to import the header files if I want to use class A in class B. Well for this game I have two custom classes, a Location class and an Exit class. The Location class needs to know about what Exits it has (So I have to import Exit.h if I want to use them) and the exits need to know which location it's connected to (So I have to import Location.h). It seems that I can't do this because of something called Circular Referencing (or something like that). However, if I don't do this then I get an "Expected a type" error. So I have no idea what to do. I'll show the code below.
Exit.h
#import <Foundation/Foundation.h>
#import "Location.h"
#define NORTH 0
#define SOUTH 1
#define EAST 2
#define WEST 3
#interface Exit : NSObject
#property NSString * dirName;
#property NSString * dirShortName;
#property int direction;
#property Location * connection;
-(id)initWithConnection:(Location *) loc andDirection:(int) dir;
#end
Exit.m
#import "Exit.h"
#implementation Exit
#synthesize dirName;
#synthesize dirShortName;
#synthesize direction;
#synthesize connection;
-(id)initWithConnection:(Location *)loc andDirection:(int)dir {
self = [super init];
if(self) {
direction = dir;
switch(direction) {
case 0:
dirName = #"North";
dirShortName = #"N";
break;
case 1:
dirName = #"South";
dirShortName = #"S";
break;
case 2:
dirName = #"East";
dirShortName = #"E";
break;
case 3:
dirName = #"West";
dirShortName = #"W";
break;
}
connection = loc;
}
return self;
}
#end
Location.h
#import <Foundation/Foundation.h>
#interface Location : NSObject
#property NSString * title;
#property NSString * desc;
#property NSMutableDictionary * exits;
#property BOOL final;
-(id) initWithTitle:(NSString *) _title;
-(id) initWithDescription:(NSString *) _desc;
-(id) initWithTitle:(NSString *) _title andDescription:(NSString *) _desc;
-(void) addExit:(Exit *) _exit;
#end
Location.m
#import "Location.h"
#implementation Location
#synthesize title;
#synthesize desc;
#synthesize exits;
#synthesize final;
-(void) addExit:(Exit *) _exit {
NSString * tmpName = [_exit dirName];
NSString * tmpShortName = [_exit dirShortName];
[exits setObject:tmpName forKey:tmpShortName];
}
-(NSString *)description {
NSString * tmp = [[NSString alloc] initWithFormat:#"%#\n%#\n",self.title,self.desc];
for(NSString * s in exits) {
[tmp stringByAppendingFormat:#"\n%#",s];
}
return tmp;
}
// Initialization Methods
-(id) init {
self = [super init];
if(self) {
title = #"";
desc = #"";
}
return self;
}
-(id) initWithTitle:(NSString *) _title {
self = [super init];
if(self) {
title = title;
desc = #"";
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
-(id) initWithDescription:(NSString *) _desc {
self = [super init];
if(self) {
title = #"";
desc = desc;
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
-(id)initWithTitle:(NSString *) _title andDescription:(NSString *)_desc {
self = [super init];
if(self) {
title = title;
desc = desc;
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
#end
I'm really hoping I'm not trying to do something that's impossible. I also hope my code can be made sense of and I'm not making too much of a fool of myself here ;) thanks for any advice.
EDIT:
Just reread and now understand better, you need to do #class Exit; to define the Exit class in the Location header and then you can do the same #class Location; in the Exit header in order to tell the compiler that the classes are defined. Then if you were to reference those classes in the implementation files (.m) then you would import the Exit.h file and Location.h file respectively
The rule of thumb I have started to follow, which seemed counter-intuitive to me at first is this:
In your header files, use "forward declarations" prolifically with only 2 exceptions:
headers for classes you are extending, and headers for protocols you are conforming to; and only do #import directives in your .m files.
This should resolve the circular reference error; it did mine.
See here, and do a 'find' for the word "forward".
i need to build an application that define an array that should be made of image items.
every image iten has an image, a name and a photographer name.
i build my image item class and i want you to check if my define is correct and good(i just start to learn objective c).
i want you to emphasize on the set's methods.
here is the photoitem.h:
#import <Foundation/Foundation.h>
#interface photoItem : NSObject
{
UIImage *imageView;
NSString *photoNameLabel;
NSString *photographerNameLabel;
UIButton *viewPhoto;
}
#property(readonly) NSString *name;
#property(readonly) NSString *nameOfPhotographer;
#property(readonly) UIImage *imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer: (NSString*)photographerName;
#end
here is my photoitem.m:
#import "photoItem.h"
#implementation photoItem
#synthesize name;
#synthesize nameOfPhotographer;
#synthesize imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName
{
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
return self;
}
-(void) setName:(NSString *)name
{
photoNameLabel = name;
}
-(void) setNameOfPhotographer:(NSString *)nameOfPhotographer
{
photographerNameLabel = nameOfPhotographer;
}
-(void)setImageItem:(UIImage *)imageItem
{
imageView = imageItem;
}
#end
i hope you could fix my errors(if there are some).
thanks.
Two problems come to mind:
1) -(id)makePhotoItemWIthPhoto:name:photographer: might be better as -(id)initWithPhoto:name:photographer:. Otherwise the caller needs to alloc and init an object first so that self is valid, then call your method. At that point, the return of self doesn't make sense.
Example:
-(idinitWithPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName {
self = [super init];
if (self) {
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
}
return self;
}
2) The three readonly properties don't seem to have any purpose since they have no connection to the variables that you initialize in the makePhotoItemWIthPhoto: method.
Members, scholars, code gurus.
My background is far from any computer programming thus my question may seems basic and somewhat trivial to you. Nevertheless it seems that I can't put my head around it.
I have googled and searched for the answer, just to get myself confused even more.
With that, I would kindly ask for a simple explanation suitable for a non technical person such as myself and for other alike arriving to this thread.
I have left a comment with the text "Here is the issue" below, referring to my question.
// character.h
#import <Foundation/Foundation.h>
#interface character : NSObject {
NSString *name;
int hitPoints;
int armorClass;
}
#property (nonatomic,retain) NSString *name;
#property int hitPoints,armorClass;
-(void)giveCharacterInfo;
#end
// character.m
#import "character.h"
#implementation character
#synthesize name,hitPoints,armorClass;
-(void)giveCharacterInfo{
NSLog(#"name:%# HP:%i AC:%i",name,hitPoints,armorClass);
}
#end
// ClassAtLastViewController.h
#import <UIKit/UIKit.h>
#interface ClassAtLastViewController : UIViewController {
}
-(void)callAgain;
#end
// ClassAtLastViewController.m
#import "ClassAtLastViewController.h"
#import "character.h"
#implementation ClassAtLastViewController
- (void)viewDidLoad {
//[super viewDidLoad];
character *player = [[character alloc]init];
player.name = #"Minsc";
player.hitPoints = 140;
player.armorClass = 10;
[player giveCharacterInfo];
[player release];
// Up until here, All peachy!
[self performSelector:#selector(callAgain) withObject:nil afterDelay:2.0];
}
-(void)callAgain{
// Here is the issue, I assume that since I init the player again I loss everything
// Q1. I loss all the data I set above, where is it than?
// Q2. What is the proper way to implement this
character *player = [[character alloc]init];
[player giveCharacterInfo];
}
Many thanks in advance, Kindly remember that my background is more related to Salmons breeding than to computer code, try and lower your answer to my level if it's all the same to you.
I loss all the data I set above, where is it than?
It's been freed (released). Notice:
character *player = [[character alloc]init]; // You create the character here...
// And then you initialize it...
// Then later...
[player release]; // You release it.
Even if you didn't release it, you still wouldn't be able to access it from callAgain, because you declare player in viewDidLoad. If you want it available to all methods in instances of ClassAtLastViewController, you should make it an instance variable.
What is the proper way to implement this
I don't know the specifics of your usage, but I imagine you want something like this:
#interface ClassAtLastViewController : UIViewController {
character *player; // Make player an instance variable
}
-(void)callAgain;
#end
#implementation ClassAtLastViewController
- (void)viewDidLoad {
//[super viewDidLoad];
player = [[character alloc]init]; // Initialize the instance variable
player.name = #"Minsc";
player.hitPoints = 140;
player.armorClass = 10;
[player giveCharacterInfo];
[self performSelector:#selector(callAgain) withObject:nil afterDelay:2.0];
}
-(void)callAgain{
[player giveCharacterInfo];
}
When you init the character in your callAgain function, you are actually creating a second instance of character. Even though the character created in callAgain has the same name as the character you created in viewDidLoad, it is a completely separate variable. The character you init in viewDidLoad only exists in the scope of the viewDidLoad function.
Also, when you call [player release] in viewDidLoad, you are removing that character instance from memory.
I think what you want to do is to create an instance variable that can be used throughout your class:
// ClassAtLastViewController.h
#import <UIKit/UIKit.h>
#interface ClassAtLastViewController : UIViewController {
character *player;
}
-(void)callAgain;
#end
// ClassAtLastViewController.m
#import "ClassAtLastViewController.h"
#import "character.h"
#implementation ClassAtLastViewController
- (void)viewDidLoad {
//[super viewDidLoad];
player = [[character alloc]init];
player.name = #"Minsc";
player.hitPoints = 140;
player.armorClass = 10;
[player giveCharacterInfo];
[self performSelector:#selector(callAgain) withObject:nil afterDelay:2.0];
}
-(void)callAgain{
// Here is the issue, I assume that since I init the player again I loss everything
// Q1. I loss all the data I set above, where is it than?
// Q2. What is the proper way to implement this
[player giveCharacterInfo];
}