I've read the other threads about arrays of UIImageView-objects, but I can't get it to work. Note that I'm completely new to Objective-c.
I have some UIImageView objects declared in the .h - file like this:
#property (weak, nonatomic) IBOutlet UIImageView *obj1;
I also have an array declared at the same place:
#property (weak, nonatomic) IBOutlet NSArray *arr;
In the viewDidLoad-method I want to add obj1 to arr. How can I do that?
I've tested to initialize arr and add objects to it, but it doesn't work.
_arr = [[NSMutableArray alloc] init];
[_arr addObjects: obj1];
Hank
make sure in debugger that _arr is not nil. You need to declare it as
#property (strong, nonatomic) NSMutableArray *arr;
since arr is not a ui control on view controller, you do not need to add IBOutlet.
Also, your view controller posses it, so it should be strong rather than weak.
You also should use NSMutableArray instead of NSArray, since only the first one can be used for adding objects, and this is how you say to the compiler of the type of this object
Change
#property (weak, nonatomic) IBOutlet NSArray *arr;
to
#property (strong, nonatomic) NSMutableArray *arr;
If you want to add UIImageView Object in your Array you can do that.But the thing is if your want to add object in NSArrar after its initialization than you have to declare NSMutableArray as you can add and remove object in that than the addObject method would work.
Also their is no need to declare NSArray of NSMutableArray as IBOutlet.
Related
I have class, that have several properties, it look like this:
#interface PlaceHolder : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *webPage;
#property (strong, nonatomic) NSNumber *latitude;
What i need is, to create an array, that hold objects of that class. Obvious, properties will not be nil and will be different each time. So, that array must have several hundreds of PlaceHolder object, and it should be possible to get information for any of that object and it properties. But, when i try to create that array, in NSLog i see that it contain only (NULL) objects. This is how i try too add object to array:
In header i wrote:
#property (strong, nonatomic) PlaceHolder *place;
Then:
self.place = [[PlaceHolder alloc]init];
self.place.name = nameString;
NSLog(#"%# name???", self.place.name);
[self.placeObjectsArray addObject:self.place];
self.place.name is not nil, and still, array is empty. Well, its not true, it not empty but, it only contains (null) objects. How to fill array with objects of my class?
Any advice would be appreciated, thanks!
UPDATED:
I init array like this -
-(id)initWithDelegate:(id)delegateObj{
...
self.placeObjectsArray = [NSMutableArray array];
...
return self;
}
UPDATED: Now, when i try to init-alloc array in same method (instead of setting #property and strong relation) i can see it in NSLog. I wonder why it won't happen when i use my array, that set as property..
You need to alloc-init your Mutable Array ;
NSMutableArray *myArray = [[NSMutableArray alloc]initWithObjects:self.place,nil];
or simply
NSMutableArray *myArray = [[NSMutableArray alloc]init];
Then you could add objects with a for loop or whatever you need, using the following :
for ( YOURINSTRUCTION )
{
[myArray addObject:YOUROBJECT]
}
I recommend to lazy instantiate the array, that way it will only get instantiated when really needed. Since you are setting the array as a property, you can override the getter method for it like this:
- (NSMutableArray *)placeObjectsArray
{
if (!_placeObjectsArray) _placeObjectsArray = [[NSMutableArray alloc] init];
return _placeObjectsArray;
}
With this, you can call [self.placeObjectsArray addObject:self.place] anywhere in your code and the array will always be initialized when needed.
I have two custom classes: FSGame and FSEvent. FSGame has an ivar that should be an NSMutableArray of FSEvent objects.
Here's FSGame.h:
#interface FSGame : NSObject
#property (strong, nonatomic) NSMutableArray *players;
#property (strong, nonatomic) NSString *startTime;
#property (strong, nonatomic) NSString *endTime;
#property (strong, nonatomic) NSMutableArray *gameEvents;
#end
And here's my FSEvent.h:
#interface FSEvent : NSObject
#property NSInteger ID;
#property NSInteger pointTo;
#end
I use
#property (strong, nonatomic) FSGame *game;
to keep an instance of FSGame in my AppDelegate. Then, in my application:didFinishLaunchingWithOptions: I create an instance of FSGame so it can be filled throughout the "game".
_game = [[FSGame alloc] init];
Then, in one of my view controllers, I alloc and init an instance of FSEvent and then attempt to add it to the .gameEvents array:
[appDelegate.game.gameEvents addObject: event];
But something seems to go wrong here because if, from another view controller, I try to get access to that object, it will be nil:
FSEvent *previousEvent = [appDelegate.game.gameEvents lastObject];
if (previousEvent == nil) {
NSLog(#"previousEvent is NIL!");
}
What am I missing here?
It's hard to say with the code that's been shown, but it's possible that you never create the mutable array gameEvents and it's nil when you try to access it or add events to it. Make sure you're creating it somewhere, probably in FSGame's -init:
_gameEvents = [NSMutableArray array];
You need to instantiate the arrays, this does NOT happen when you use
_game = [[FSGame alloc] init];
Try using a lazy instantiation in your FSGame class, add this code
-(NSMutableArray *)gameEvents{
if(!_gameEvents){
_gameEvents = [NSMutableArray array];
}
return _gameEvents;
}
And that should be enough, also make sure of accesing by self.gameEvents and not by _gameEvents!
_game.gameEvents is a pointer to a mutable array. It is not initialized, so its value is nil. There are no any mutable array to add objects to.
You can create it when needed writing this method in FSGame.m:
-(NSMutableArray*) gameEvents {
if ( ! _gameEvents ) {
_gameEvents = [NSMutableArray array];
}
return _gameEvents ;
}
I have an object called SCPFAd and it is declared in its header file as follows:
#interface SCPFAd : NSObject
#property (strong, nonatomic) NSArray *imageURLs;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *price;
#property (strong, nonatomic) NSString *longDescription;
#property (strong, nonatomic) SCPFLocation *location;
#property (strong, nonatomic) SCPFCategory *category;
#property (strong, nonatomic) NSArray *properties;
#property (readonly, strong, nonatomic) NSString *sellerID;
#property (readonly, strong, nonatomic) NSString *timePosted;
- (id)initWithRawData:(NSDictionary *)rawData;
- (BOOL)displaysPrice;
#end
In the implementation file, I have an SCPFAd extension declared this way:
#interface SCPFAd ()
{
NSMutableDictionary *_rawData;
NSMutableArray *_imageURLs;
NSString *_title;
NSString *_price;
NSString *_longDescription;
SCPFLocation *_location;
SCPFCategory *_category;
NSMutableArray *_properties;
}
#property (strong, nonatomic) NSDictionary *rawData;
#property (strong, nonatomic) NSString *sellerID;
#property (strong, nonatomic) NSString *timePosted;
#property (strong, nonatomic) NSString *adID;
#end
I deliberately redeclared the properties rawData, imageURLs, and properties as instance variables because I want external objects to access or assign them as immutable types, but I'll be changing them internally.
What I don't understand is why, when I override the setters, I get a compiler error that says it can't find the variables _title, _price, _longDescription, _location, and _category. The error goes away when I redeclare title, price, longDescription, location, and category as above, but I see it as unnecessary--nothing in the class extension changes their external declarations.
This is how I'm overriding setTitle, for example:
- (void)setTitle:(NSString *)title
{
_title = title;
_rawData[#"name"] = title;
}
- (NSString *)title
{
if (!_title) {
_title = _rawData[#"name"];
}
return _title;
}
If I comment out NSString *_title; in the extension, the compiler says it can't find _title in the first line of the setter, and wherever it occurs in the getter. The getter used to work just fine, though, even without the redeclaration.
If you declare a property and then override both the getter and setter, it won't auto-synthesize the property. But you can just add a line to synthesize it to your implementation:
#synthesize title = _title;
As for having a property be an immutable type, and its backing instance variable be mutable, you're going to have an issue when from outside your class the immutable type is assigned to it, and you treat it as the mutable version, because it won't respond to the methods to mutate it. For example, you assign an NSArray to a variable, then try to treat it as an NSMutableArray, it won't work.
If you implement a getter, the compiler doesn't automatically create an ivar.
This is for a good reason. The property may (and, in my experience, usually is) created on request and returned, so in that case no instance variable is needed to store it and it would add a significant memory overhead to classes with a large number of such properties if every getter had an associated ivar.
One other comment. This:
NSMutableDictionary *_rawData;
// ...
#property (strong, nonatomic) NSDictionary *rawData;
May cause you problems. If rawData is set with an immutable dictionary, it will raise an exception when you attempt to mutate it later. Make sure you copy it on assign using -mutableCopy. (I assume you aren't copying it because it's marked strong, not copy. If you are, it's fine)
When you override the setter and getter (not just the getter), Xcode assumes you want complete control and doesn't create the backing store (the _title). You have to do it yourself with
#synthesize title = _title
If you implement a getter and a setter for a read-write property, or a getter for a read-only property then Clang (Xcode) will not synthesise the backing instance variable - see Apple's Encapuslating Data, note in the section You Can Implement Custom Accessor Methods.
You are implementing both the setter and the getter so you must provide your own instance variable if needed.
I'm trying to make a deep copy of an NSMutableArray whose objects are instances of a custom class similar to this:
#interface CustomParent : NSObject
#property NSInteger Id;
#property (strong, nonatomic) NSString *IdStr;
#property (weak, nonatomic) NSDate *Date;
#property (strong, nonatomic) NSMutableArray *CustomChildren;
#property (strong, nonatomic) CustomType *Type;
#property float Value;
#end
I know there are lots of posts dealing with copying objects, but I donĀ“t find examples for getting a complete copy of objects with collection members or properties. NSMutableArray *dstArray = [[NSMutableArray alloc] initWithArray:srcArray copyItems:YES]; raises an exception involving the copyWithZone method.
How can I do this? Thanks!
In order to deep copy the content of the array
[[NSMutableArray alloc] initWithArray:srcArray copyItems:YES];
will send copyWithZone: to every object inside the collection. If they don't respond to this selector, you'll get a crash.
Have your CustomParent class to conform to the NSCopying protocol and you're done.
Here's some extra info on how do achieve it: Implementing NSCopying
My project is ARC-enabled, I have two models something like:
Category:
#interface Parent : NSObject
#property (assign, nonatomic) Category *parent;
#property (strong, nonatomic) NSArray *children;
#property (strong, nonatomic) NSArray *subCategories;
#end
Product:
#interface Child : NSObject
#property (strong, nonatomic) NSString *name;
#property (assign, nonatomic) Category *category;
#end
To avoid reference loop, and to deploy on iOS 4.x, I set the parent property of Child as assign but not weak.
From JSON data, I can get a tree containing my categories and products. When I use the tree to enumerate, all the things went perfect. And I have a recursion method to search products with name matching a keyword. The method is something as followed:
- (NSArray *)productsWithKeyword:(NSString *)keyword
{
NSMutableArray *filteredProducts = [NSMutableArray array];
for (Category *cat in self.children) {
NSArray *subResult = [cat productsWithKeyword:keyword];
[filteredProducts addObjectsFromArray:subResult];
}
return [filteredProducts copy];
}
The problem is when I'm using the returned array, I can get the right category property. But from the second time and on, the category property becomes 0 (caught from debugger).
So, what's the problem? And what's the fix to the problem?
To avoid a reference loop you want to make one object have a weak reference to the other not have both with weak references to each other. I think what's happening in this case is that the product doesn't own the category and the category doesn't own the product, so ARC releases both of the objects assuming no one claims them.
FIX: make one of these objects the "parent" of the other.