I am trying to get a NSMutableArray from another class. I am trying to reference that ViewController that contains the array. Lets say, a has the array I want. b is the class I am going to get that array in.
a.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
a.m:
#synthesize selectedCells;
AppDelegate.h:
#property (strong, nonatomic) a *create_challengeDelegate;
AppDelegate.m:
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
Right here when I try to reference that ViewController I get an error saying:
Initializer element is not a compile-time constant
I assume it has something to do with it not being able to see the ViewController.
In my b.m:
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
app.create_challengeDelegate.selectedCells
My issue is working with initializing the ViewController in the delegate.
Suggestions and thoughts on that?
My suggestion is that you create the selectedCells array in you AppDelegate and send it to A. E.g. in:
AppDelegate.h
#property (nonatomic, strong) NSArray *selectedCells;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.selectedCells = [NSMutableArray array];
A *viewA = [[a alloc] initWithNibName:#"a" bundle:nil selectedCells:self.selectedCells];
[...]
}
a.h
#property (nonatomic, strong) NSMutableArray *selectedCells;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells;
a.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.selectedCells = cells;
}
return self;
}
b.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *selectedCells = app.selectedCells;
This way, whenever you make changes to selectedCells it will keep it in the array you send there since selectedCells is a refference to an object which is created in your AppDelegate.
Otherwise, what you are trying to do is access a view which might no longer be available in the memory since the view might have been deallocated.
Also, you create the instance of a but its not set to the AppDelegate's instance of a, its a seperate instance.
Instead of
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
You should have
#synthesize create_challengeDelegate;
self.create_challengeDelegate = [[a alloc] init];
I still strongly recommend you do not access your view this way though.
P.s. #synthesize is no longer necessary.
Edit
This is a better solution.
Selection.h
/**
* The entity for selections
*/
#interface Selection : NSObject
/**
* The Shared Instance
*
* #return Selection The instance
*/
+ (Selection *)sharedInstance;
/**
* Add an item to the selections
*
* #param object id The object to add
*/
- (void)addSelection:(id)object;
/**
* Remove an item from the selections
*
* #param object id The object to remove
*/
- (void)removeObject:(id)object;
/**
* Get the selections
*
* #return NSArray The array with the current selection objects
*/
- (NSArray *)getSelections;
#end
Selection.m
#import "Selection.h"
#interface Selection ()
#property (nonatomic, strong) NSMutableArray *selections;
#end
#implementation Selection
#pragma mark - Public methods
+ (Selection *)sharedInstance
{
static Selection *sharedInstance = nil;
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
#pragma mark - Private methods
- (id)init
{
if (self = [super init])
{
self.selections = [NSMutableArray array];
}
return self;
}
#pragma mark - Manage selection
- (void)addSelection:(id)object
{
[self.selections addObject:object];
}
- (void)removeObject:(id)object
{
[self.selections removeOjbect:object];
}
- (NSArray *)getSelections
{
return [NSArray arrayWithArray:self.selections];
}
Now you can assess your shared singeton in A and/or B by accessing:
[[Selection sharedInstance] addObject:myObject];
[[Selection sharedInstance] removeObject:myObject];
NSArray *array = [[Selection sharedInstance] getSelections];
Is this line inside a method:
a *create_challengeDelegate = [[a alloc]init];
If not that would probably explain your issue. If that's not the problem it might be helpful to post more complete code.
Related
There are two view controllers.One to add items,the other one to display(table view).All items are stored in an NSMutableArray.But every time I unwind to add item and when I go back to the table view, there is only the newest item left.
Code:
- (void)viewDidLoad {
[super viewDidLoad];
[self addData];
}
- (void)addData {
if (!self.items) {
self.items = [[NSMutableArray alloc] init];
}
[self.items addObject:self.textFromFirst];
}
// textFormFirst is an NSString which received from the previous view controller
add view controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UINavigationController *nav = [segue destinationViewController];
SecondController *second = [nav.viewControllers objectAtIndex:0];
second.textFromFirst = [self getText]; // get inputed string
self.aTextField.text = #"";
}
You can share an NSMutableArray between your two view controllers. Just use a property:
MainViewController:
// .m
#interface MasterViewController ()
#property (nonatomic, strong) NSMutableArray *items;
#end
#implementation MasterViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.items = [NSMutableArray array];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ShowDetail"]) {
DetailViewController *controller = (DetailViewController *)[segue destinationViewController];
controller.items = self.items;
controller.textFromFirst = #"This is a test";
}
}
#end
AddViewController:
// .h
#interface DetailViewController : UIViewController
#property (nonatomic, copy) NSString *textFromFirst;
#property (nonatomic, strong) NSMutableArray *items;
#end
// .m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self addData];
}
- (void)addData {
[self.items addObject:self.textFromFirst];
}
These is a sample project: https://www.dropbox.com/s/ymum4zivgi0z688/TestUnwindSegue.zip?dl=0
I do not know why are you using array in AddViewController. Maybe you are prefer use unwind segue, like so:
// MainViewController.m
- (IBAction)saveItem:(UIStoryboardSegue *)segue {
DetailViewController *detailVC = segue.sourceViewController;
[self.items addObject:detailVC.textFromFirst];
}
In the case, you don't need to share an NSMutableArray to AddViewController.
How are you persisting the NSMutableArray between view controllers? If you need to persist one array across your entire application I would suggest you use a singleton. A singleton is an object that each instance has only 1 memory address, thus is always the same object. You could accomplish this by doing the following:
1) Press CMD+N and select "Cocoa Touch Class" / subclass NSMutableArray
2) Go to that class' .m file and add the singleton in init
static YourNSMutableArray *highlander;
#implementation YourNSMutableArray
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
highlander = [super init];
});
return highlander;
}
3) Now whenever you create a new instance in any of your view controllers like this
if (!self.items) {
self.items = [YourNSMutableArray new];
}
[self.items addObject:self.textFromFirst];
self.items will always have the stored self.textFromFirst because any YourNSMutableArray you create in your app will always be the same object.
I'm trying to understand why one object is not deallocating in my code and how to fix this. I have ARC enabled for all classes and one external library IndoorsSDK-iOS (3.3.0).
I the following code the property del2 is not deallocated (The dealloc method of the IndoorsTestDelegate is not called). The del2 property is passed to Indoors instance in registerLocationListener: message and is removed from it during dealloc in view controller. The dealloc method of View Controller is called when the View Controller is Pushed back in Navigation Controller stack. The del1 dealloc method from IndoorsTestDelegate also is called but del2 dealloc method is not called. And my question is why del2 dealloc method is not called and how can I fix this. I don't have sources from external library.
This is the View Controller code:
#import "IndoorsTestVC.h"
#import "IndoorsTestDelegate.h"
#import <Indoors/Indoors.h>
#import <IndoorsSurface/ISIndoorsSurfaceViewController.h>
#interface IndoorsTestVC ()
#property (strong, nonatomic) IndoorsTestDelegate *del1;
#property (strong, nonatomic) IndoorsTestDelegate *del2;
#end
#implementation IndoorsTestVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.del1 = [[IndoorsTestDelegate alloc]
initWithController:self
andName:#"Del1"];
self.del2 = [[IndoorsTestDelegate alloc]
initWithController:self
andName:#"Del2"];
__unused Indoors *indoors = [[Indoors alloc]
initWithLicenseKey:API_KEY
andServiceDelegate:self.del1];
[[Indoors instance] enableEvaluationMode:NO];
[[Indoors instance] setLogLevel:IDSLogLevelWarning];
[[Indoors instance] registerLocationListener:self.del2];
ISIndoorsSurfaceViewController *surfaceVC = [[ISIndoorsSurfaceViewController alloc] init];
surfaceVC.delegate = self.del1;
// add surfaceVC as a child view controller
[self addChildViewController:surfaceVC];
[self addSubview:surfaceVC.view
withEqualSizeLikeParent:self.view];
[surfaceVC didMoveToParentViewController:self];
// load building
[surfaceVC loadBuildingWithBuildingId:1234];
}
- (void)dealloc {
[[Indoors instance] removeLocationListener:self.del2];
NSLog(#"Dealloc test controller %#", _title);
}
This is the IndoorsTestDelegate.h:
#import <Foundation/Foundation.h>
#import <Indoors/Indoors.h>
#import <IndoorsSurface/ISIndoorsSurfaceViewController.h>
#interface IndoorsTestDelegate : NSObject <IndoorsServiceDelegate, ISIndoorsSurfaceViewControllerDelegate, IndoorsLocationListener>
#property (weak, nonatomic) UIViewController *controller;
#property (strong, nonatomic) NSString *name;
- (instancetype)initWithController:(UIViewController *) controller
andName:(NSString *)name;
#end
And IndoorsTestDelegate.m:
#import "IndoorsTestDelegate.h"
#implementation IndoorsTestDelegate
- (instancetype)initWithController:(UIViewController *)controller
andName:(NSString *)name
{
self = [super init];
if (self) {
_controller = controller;
_name = name;
}
return self;
}
- (void)dealloc {
// This method is called for del1 property
// but for del2 not. Why?
NSLog(#"Dealloc %#", _name);
}
// The protocols methods are empty in testing delegate
#pragma mark IndoorsLocationListener
- (void)zonesEntered:(NSArray *)zones {}
- (void)positionUpdated:(IDSCoordinate *)userPosition {}
- (void)contextUpdated:(IDSContext *)context {}
- (void)changedFloor:(int)floorLevel withName:(NSString *)name {}
- (void)weakSignal {}
- (void)orientationUpdated:(float)orientation {}
#pragma mark ISIndoorsSurfaceViewControllerDelegate
- (void)indoorsSurfaceViewController:(ISIndoorsSurfaceViewController *)indoorsSurfaceViewController isLoadingBuildingWithBuildingId:(NSUInteger)buildingId progress:(NSUInteger)progress {}
- (void)indoorsSurfaceViewController:(ISIndoorsSurfaceViewController *)indoorsSurfaceViewController didFinishLoadingBuilding:(IDSBuilding *)building {}
- (void)indoorsSurfaceViewController:(ISIndoorsSurfaceViewController *)indoorsSurfaceViewController didFailLoadingBuildingWithBuildingId:(NSUInteger)buildingId error:(NSError *)error {}
#pragma mark - IndoorsSurfaceServiceDelegate
- (void)onError:(IndoorsError *)indoorsError {}
- (void)bluetoothStateDidChange:(IDSBluetoothState)bluetoothState {}
- (void)locationAuthorizationStatusDidChange:(IDSLocationAuthorizationStatus)status {}
- (void)connected {}
#end
I have simple example:
NavViewController
ViewController
ViewController2
In ViewController:
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
NSArray *array;// Neither in #interface nor in #implementation
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
array = [[NSArray alloc] initWithObjects:#"12345", nil];
ViewController2 *vc = [[ViewController2 alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
In ViewConroller2:
#import "ViewController2.h"
#interface ViewController2 ()
#end
NSArray *array;
#implementation ViewController2
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#",array);
}
I don't understand why my array in viewController2 passed data from viewController?
Can explain this?
Guys I know how pass data to another viewController with property. I want to understand why, in this case, the data is transferred!
Because you've declared NSArray *array; as a Global variable. And, as long as the variable is defined somewhere in a source file, the linker will be able to find it and appropriately link all the references in other source files to the definition.
we also declare global variables using extern
extern int GlobalVar;
Here, externtells the compiler that this is just a declaration that an object of type int identified by GlobalVar exists and linker's job to ensure.
In one of your source file, you could say
int GlobalVar = 7;
I believe this is the reason in your case.
When you declare your variable outside #interface or #implementation it is considered to be static variable hence it worked. keep it inside would not work.
#interface ViewController ()
{
NSArray *array;
}
#end
try this instead of your code
Super basic questions, which i'm having problems with.
How do i add multiple objects to my NSMutableArray? (Now i only add one with self.itemsArray[0] = iPhoneItem; )
How do i retrieve for the first objects property (itemName)?
I have a calss: Item - which looks like follows.
Item.h
#interface Item : UITableViewController
#property (nonatomic, copy) NSString *itemTitle;
- (id)initWithItemTitle:(NSString *)aTitle;
#end
Item.m
#interface Item ()
#end
#implementation Item
- (id)initWithTitle:(NSString *)aTitle {
self = [super init];
if (self) {
self.itemTitle = aTitle;
}
return self;
}
#end
And now i just want to create a few objects, add them in to an NSMutableArray and retrieve the itemTitle property.
ViewController.m - (.h has no additional changes from standard "create singel view application"
#import "ViewController.h"
#import "Item.h"
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *itemsArray;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Item *iPhoneItem = [[Item alloc] initWithItemTitle:#"iPhone"];
Item *iPadItem = [[Item alloc] initWithItemTitle:#"iPad"];
Item *macBookPro = [[Item alloc] initWithItemTitle:#"MacBookPro"];
self.itemsArray[0] = iPhoneItem;
NSLog(#"%#", self.itemsArray[0].itemTitle); //How would i do this?
}
#end
Best regards, iOS-rookie.
You can simply check iOS reference, no need to ask questions: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableArray/addObjectsFromArray:
Anyway, here is an example:
[itemsArray addObjectsFromArray: #[obj1, obj2]]; //adding multiple objects
((Item *)[itemsArray firstObject]).itemTitle //get title of your object
It is bad practice to access properties for an object in an array directly like :
self.itemsArray[0].itemTitle
It is cleaner to:
Item iPhoneItem = (Item)[itemsArray objectAtIndex:0];
NSLog(#"%#", iPhoneItem.itemTitle);
Also keep in mind that you can use [itemsArray firstObject]; and [itemsArray lastObject];
It works if I put it in viewDidLoad but I can't imagine that's the best place to do it. I tried putting it here:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// HERE
}
return self;
}
But that didn't work. Where should I put it?
In this example I'm talking about creating the NSMutableArray (alloc and initing it) for this class.
You could go with a lazy-loading technique as A-Live suggests in the comments, such that your array will be initialized when you actually need it. The idea is that in a property getter, you first check to see if your array was initialized. If not, initialize then return it.
Example
Note: This is a crude and untested example, and you may need to make
the necessary changes depending on whether or not you use ARC.
MyViewController.h
#interface MyViewController : UITableViewController
{
NSArray *_myArray;
}
#property (nonatomic, readonly) NSArray *myArray;
-(void)doSomething;
#end
MyViewController.m
#interface MyViewController()
-(NSArray *)fetchArrayData;
#end
#implementation MyViewController
#synthesize myArray = _myArray;
#pragma mark - Property Getter
-(NSArray *)myArray
{
if (_myArray==nil)
_myArray = [[self fetchArrayData] retain];
return _myArray;
}
#pragma mark - Cleanup
-(void)dealloc
{
[_myArray release];
[super dealloc];
}
#pragma mark - Instance Methods
-(void)doSomething
{
NSLog(#"myArray: %#", self.myArray);
}
#pragma mark - Private Methods
-(NSArray *)fetchArrayData
{
NSArray *arrayData = [NSArray arrayWithObjects:#"Apples", #"Oranges", nil];
return arrayData;
}
#end
You have 4 initialization methods:
- (id)init
- (id)initWithCoder:(NSCoder *)aDecoder
- (id)initWithStyle:(UITableViewStyle)style
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
It all dependes on how you're instantiating the class.
You can initialize your instance variables in initWithStyle: if you create your controller programmaticaly or in initWithCoder:/awakeFromNib if it's loaded from nib/storyboard.