I'm building an app whereas I have a UITableViewController set up as static and grouped. I'm using StoryBoard.
I have a breakpoint in my code where self looks like an array as it says "0 Objects" while it's a UITableViewController. Does anyone have a clue on why this is? Same happens if I use the class name as a cast. Just need an explanation for this.
V[0] is supposed to be an NSMutableArray, I init it like this, oldArray contains 3 objects (valid and correct objects)
_newArray = [[NSMutableArray alloc] initWithArray:oldArray];
But it doesn't look correct, neither does it work properly or log any objects. It logs out as nil when doing po _newArray
Thanks!
Update: fix
I changed it to a property instead of declaring it in #interface like so:
#property (nonatomic, strong) NSMutableArray *newArray;
and populated it using a function like so:
self.newArray = [NSMutableArray arrayWithArray:[self myArray]];
whereas myArray is a function with NSArray as return type. Looks like that fixed it, any comments on that?
Related
I'm trying to create a bookmark page for my app. basically, I want to be able to click on a button in my UIViewController, and have the url of the website I'm currently viewing be saved as a cell in a UITableViewController. The problem is I'm not so sure of the best way to implement this.
I think the way to do it would be to have a mutable array in my tableviewcontroller which I've created here:
#import <UIKit/UIKit.h>
#interface FavoriteViewController : UITableViewController
#property (strong, atomic) NSMutableArray *tableItems;
#end
I could then use the button in the viewController to populate the array. However when I try something like this:
- (IBAction)fave:(id)sender {
[FavoriteViewController.tableItems addObject:[NSString self.faveURL]];
}
I receive an error that property tableItems is not found on FavoriteViewController. Not sure why this is. Anyone have a solution?
Access properties through object instead of directly calling by class name.
- (IBAction)fave:(id)sender {
// If accessing from other class
FavoriteViewController *favVC = ------
[favVC.tableItems addObject:[NSString self.faveURL]];
}
- (IBAction)fave:(id)sender {
// If accessing from same class where you defined your variable
[self.tableItems addObject:[NSString self.faveURL]];
}
If you want to access using class name then you have to static/class methods instead of properties.
[self.tableItems addObject:[NSString self.faveURL]];
You're accessing a class variable with your current code, but tableItems is an instance variable. So if the function fave is in the FavoriteViewController class, then you need to reference self when accessing the instance variable.
I'm can see that there is a lot of questions regarding this already, but none of them seems to have given me an explanation to why I cannot access my array from another class.
Here's where I wan't to access the array
(XYZPaymentViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
XYZMateOverviewViewController *test = [[XYZMateOverviewViewController alloc] init];
NSMutableArray *t = test.mates;
NSLog(#"Count of ThatArray: %d", [t count]);
}
Nomatter what - the log writes out 0 !!
In another class (XYZMateOverviewViewController.h) I declare the array
#property (retain) NSMutableArray *mates;
I synthesize the array in the implementation area in XYZMateOverviewViewController.m
#synthesize mates;
I hope you can help me understand what I'm doing wrong :)
If you want to access an array from another ViewController you have to pass the array between the ViewControllers.
For example:
You have two ViewControllers. 'A' and 'B', so u have to do this:
In the 'A' ViewController, is where you have the array that you want to send to another ViewController. Then create an instance of 'B' ViewController and send the array to him:
CODE OF 'A' ViewController:
NSMutableArray *arrayToSend = [[NSMutableArray alloc]init];
BViewController *bViewController = [[BViewController alloc]initWithNibName:#"BViewController" bundle:nil];
BViewController.arrayReceived = arrayToSend;
[self.view addSubView:BViewController.view];
After, in your BViewController you just have to take your arrayReceived and use it.
CODE OF 'B' ViewController:
NSLog(#"%#", [arrayReceived objectAtIndex:0]); // For example.
Hope i did help you, if u have any question tell me.
What are you doing with the mates array in the XYZMateOverviewViewController init method?
If you're not adding any data to it a count of 0 is just right.
With the line
XYZMateOverviewViewController *test = [[XYZMateOverviewViewController alloc] init];
you are creating a completely new instance of XYZMateOverviewViewController—one that has nothing to do with any other part of your application*. What you should be doing instead is getting a reference to the already-set-up instance of the view controller. That instance presumably has its mates property set up with the data you want.
The general topic of sharing data between different parts of your application—and between different view controllers, in particular—should be covered somewhere toward the beginning of any “introduction to iOS programming” book or tutorial.
* Unless you’re doing something funky with singletons or shared state, of course, but I doubt that’s the case here.
Do you alloc/init the mutable array in the init method?
I'm using JSONModel in my iOS app and i'm facing some warnings, let me explain myself a bit.
Let's say i have this JSONModel
CTVContact.h
#interface CTVContact : JSONModel
#property (nonatomic, strong) NSArray<Optional, CTVPhone> *phone;
#end
CTVContact.m
NSMutableArray *phones = [[NSMutableArray alloc] init];
for(NSString *p in personPhones) {
CTVPhone *phn = [[CTVPhone alloc] init];
phn.original = p;
[phones addObject:phn];
}
phone = [NSArray arrayWithArray:phones];
Basically it all works like a charm but i get a warning stating the following:
Incompatible pointer types assigning to 'NSArray<Optional,CTVEventParticipant> *' from 'NSArray *'
How can i go around that warning? I can't find the right way to assign all the array values to the phone Array without that warning.
Thanks a lot in advance!
NSArray<Optional, CTVPhone> *phone; defines a variable that takes an array which conforms to 2 protocols. If you try and set that a variable (phone) to an array that doesn't state that it conforms to those protocols then you will get a compile warning.
phone = [NSArray arrayWithArray:phones]; just creates a 'plain' array, with no special protocols implemented. So you get a warning.
Probably the correct thing to do is to remove the protocols from the #property definition. Unless you have an NSArray subclass which conforms to those protocols that you should actually be using...
Alternatively, and assuming that you don't try to call any methods that might be defined in those protocols:
phone = (NSArray <Optional, CTVPhone> *)[NSArray arrayWithArray:phones];
which adds a cast that basically means to the compiler: 'trust me, it's fine'...
It looks like it may be complaining on your last line, since you're passing in an NSMutableArray when NSArray's arrayWithArray method calls for an NSArray. You can get away with this by calling copy on the phones array, as such:
phone = [NSArray arrayWithArray:[phones copy]];
I have a Singleton object that manages all my lists. We'll call it ListStore.
ListStore has a mutable array, which stores Lists.
#interface ListStore : NSObject
#property (nonatomic, copy) NSMutableArray *lists; // an array of List objects
end
Lists has a mutable array, which stores Things.
#interface Wanderlist : NSObject <NSCoding, NSCopying>
#property (nonatomic, copy) NSMutableArray *things; // an array of Thing objects
#end
At any time, a background process might go through ListStore and loop through and process all Lists, while a user might be interacting with a List.
To guard against "object was mutated while being enumerated" type errors, I do this:
// all of this is in a background thread
NSArray *newLists = [[ListStore sharedStore] lists] copy];
for (List *list in newLists) {
// yay, no more crashes, because I'm enumerating over a copied object, so the user
// can do whatever they want while I'm here
for(Thing *thing in list.things) {
// oh crap, my copy and the original object both reference the same list.things,
// which is why i'm seeing the 'mutation while enumerating" errors still
...
}
}
I originally thought that because I made a copy into newLists that all of its members would be properly copied. I now understand that not to be the case: I'm still seeing the "object was mutated while enumerated" errors, but this time it's happening on list.things.
Can I use NSCopying with my setup so that when I say:
[[ListStore sharedStore] copy];
It calls copyWithZone: on Lists, so I can then copyWithZone: on things?
I tried to set it up like this but copyWithZone: wasn't getting called.
I know I could simply say NSArray *newList = [list.things copy] but I'd like to get a better understanding of NSCopying at the very least.
Right before submitting this question I clicked on a question in SO's list of related questions, and found my solution.
Figured it doesn't hurt to post my solution.
Instead of this:
NSArray *newLists = [[ListStore sharedStore] lists] copy];
I had to do:
NSArray *newLists = [[NSArray alloc] initWithArray:[[ListStore sharedStore] lists] copyItems:true];
From the NSArray docs:
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
flag:
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying protocol. In a managed memory environment, this is instead of the retain message the object would otherwise receive. The object copy is then added to the returned array.
Once I used initWithArray:copyItems:, it automatically sent copyWithZone to all my List objects, and I was able to then manually perform a copyWithZone on list.things.
I'm trying to switch views in my app using this chunk of code:
self->variable1 = [[NSNumber alloc] initWithInt:0];
self->variable2 = [[NSMutableArray arrayWithCapacity:1];
self->variable3 = [[NSMutableArray arrayWithCapacity:1];
[self presentModalViewController:titleScreen animated:YES];
If I comment out all of the allocated variable lines, the code works fine. If it leave just 1 line in the code crashes with the "EXC_BAD_ACCESS" error. Why is this happening? The variables aren't being used at all, just declared for later use. I'm not getting any compile errors on the lines either. What am I doing wrong?
UPDATE:
Thank you everyone for the help. I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem. After a long time of fiddling I fixed it. I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
and it worked! Can someone explain why this worked and the first line didn't?
Update:
Thank you Peter Hosey for showing me my evil ways. This time I'm pretty sure it's fixed. I was storing my variable Releases in
-(void)release
I didn't realize xCode will release when it needs to. I moved all the variable releases to
-(void)Destroy
so I can release everything on MY command. Now the code works. Thanks again!
I suggest that you declare variable1, variable2, and variable3 as properties, not instance variables. Then, use self.variable1, self.variable2, and self.variable3 to access them.
The dot syntax (self.variable1, etc.) uses the memory management policy you declared on each property; the arrow syntax (self->variable1, etc.) will access the variables directly. The crash is because you created two arrays in away that doesn't leave you owning them, and then did not assign the arrays to a property that would retain them.
You may also want to upgrade your project to use ARC. Then there is no memory-management difference; assigning to the instance variables rather than the properties will not cause the object to be prematurely released, because ARC considers instance variables to be ownerships by default. You may still want to switch to using properties after you switch to ARC, but not to prevent a crash.
In response to your edit:
I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem.
Then something else was wrong.
You never did say much about the problem itself. You said you got an EXC_BAD_ACCESS, but not what statement triggered the crash or on what grounds you blamed it on the code you showed.
I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
That's the correct code, though. That's what you should be using.
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
Noooo! That code is wrong, wrong, wrong, on multiple levels.
init methods (including initWithWhatever: methods) are not guaranteed to return the same object you sent the message to. NSNumber's initWithInt: very probably doesn't.
That object creates an uninitialized NSNumber object and assigns that to the property. Then it sends initWithInt: to that object, which will return an initialized object, which can be and very probably will be a different object. Now you are holding an uninitialized object (which you will try to use later) and have dropped the initialized object on the floor.
Never, ever, ever send alloc and init(With…) in separate expressions. Always send them in the same expression. No exceptions. Otherwise, you risk holding the uninitialized object rather than the initialized object. In your case (with NSNumbers), that is almost certainly what will happen.
What you should be doing is declaring and synthesizing a strong property that owns the NSNumber object, and creating the NSNumber object in a single statement: either [[NSNumber alloc] initWithInt:] or [NSNumber numberWithInt:]. If you're not using ARC, you'll want the latter, since the property will retain the object. If you are using ARC, they're effectively equivalent.
And if you get a crash with that code, then something else is wrong, so please tell us—either in this question or in a new question—about the crash so we can help you find the true cause of it.
variable2 and variable3 are being autoreleased before you actually access them (presumably) later after presenting the modal view.
At the very least change the lines to:
self->variable2 = [[NSMutableArray arrayWithCapacity:1] retain];
self->variable3 = [[NSMutableArray arrayWithCapacity:1] retain];
or
self->variable2 = [[NSMutableArray alloc] initWithCapacity:1];
self->variable3 = [[NSMutableArray alloc] initWithCapacity:1];
variable1 should be fine.
Best would be to use #property and #synthesize so you can use dot notation:
.h
#interface MyClass : SuperClass
#property (nonatomic,retain) NSMutableArray *variable2;
#property (nonatomic,retain) NSMutableArray *variable3;
#end
.m
#implementation MyClass
#synthesize variable2,varible3;
- (void)foo {
self.variable2 = [NSMutableArray arrayWithCapacity:1];
self.variable3 = [NSMutableArray arrayWithCapacity:1];
}
#end
By default, all instance variables in objective-c have protected scope. So unless you have explicitly declared them public in your interface file as:
#interface MYClass {
#public
NSNumber *variable1;
NSMutableArray *variable2;
NSMutableArray *variable3;
}
//...
#end
then they will not be accessible using the struct dereferencing operator. This is likely the cause of those EXC_BAD_ACCESS errors.