Using pointers to adjust global objects in objective-c - objective-c

Ok, so I am working with two sets of data that are extremely similar, and at the same time, these data sets are both global NSMutableArrays within the object.
data_set_one = [[NSMutableArray alloc] init];
data_set_two = [[NSMutableArray alloc] init];
Two new NSMutableArrays are loaded, which need to be added to the old, existing data. These Arrays are also global.
xml_dataset_one = [[NSMutableArray alloc] init];
xml_dataset_two = [[NSMutableArray alloc] init];
To reduce code duplication (and because these data sets are so similar) I wrote a void method within the class to handle the data combination process for both Arrays:
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str
Now, I have a decent understanding of object oriented programming, so I was thinking that if I were to invoke the method with the global Arrays in the data like so...
[self constructData:data_set_one fromDownloadArray:xml_dataset_one withMatchSelector:#"id"];
Then the global NSMutableArrays (data_set_one) would reflect the changes that happen to "array" within the method. Sadly, this is not the case, data_set_one doesn't reflect the changes (ex: new objects within the Array) outside of the method.
Here is a code snippet of the problem
// data_set_one is empty
// xml_dataset_one has a few objects
[constructData:(NSMutableArray *)data_set_one fromDownloadArray:(NSMutableArray *)xml_dataset_one withMatchSelector:(NSString *)#"id"];
// data_set_one should now be xml_dataset_one, but when echoed to screen, it appears to remain empty
And here is the gist of the code for the method, any help is appreciated.
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str {
if ([data count] == 0) {
data = down; // set data equal to downloaded data
} else if ([down count] == 0) {
// download yields no results, do nothing
} else {
// combine the two arrays here
}
}
This project is not ARC enabled.
Thanks for the help guys!
Rob

If I understood your problem, you are trying to pass your object as call-by-reference and hoping to work as in C++/C. But Obj-C, although similar but has some different way. You have to dereference the object by using ** (double pointer) as mostly seen in NSError case, which is very rare.
Second ways is: wrap method as a block. Then put the variable in the same lexical scope as the block, and denote it by __block storage type.
Third way can be accessing your object directly by making it an ivar/property or even an singleton.

Related

Objective C - call NSNumber outside FOR loop

It seems that i didn't cover my basics enough, but I hope that You guys mabybe will be able to help here.
I need to use cateringView.status outside this loop and even in another class. This is simple BOOL value, parsed from XML with PUGIXML
- (void)dataPrepared
{
Food* food = (Food*)[[DataManager sharedInstance] dataForItem:kDataManagerItemCatering];
if (food)
{
for (CateringView* cateringView in cateringViews)
[cateringView removeFromSuperview];
[cateringViews removeAllObjects];
for (FoodItem* item in food.catering)
{
CateringView* cateringView = [CateringView new];
[cateringView.imageView loadURL:[NSURL URLWithString:item.image] session:[DataManager sharedInstance].session completion:nil];
cateringView.status = item.status;
[self addSubview: cateringView];
[cateringViews addObject: cateringView];
}
[self layoutSubviews];
}
[super dataPrepared];
}
Could You explain me how can I do that?
My header file:
(...)
#interface CateringView : UIView
#property (strong) NSNumber* status;
#end
#interface CateringPreviewCell : PreviewCell
{
NSMutableArray* cateringViews;
(...)
}
#end
Is this somebody else's code you are trying to understand? It is unclear what you are asking, but you appear to be confusing the lifetime of local variables and objects. Maybe the following will help:
The second for loop starts:
for (FoodItem* item in food.catering)
{
CateringView* cateringView = [CateringView new];
This last statements does two things:
The right hand side (RHS) creates a new object of type CateringView. The result of the RHS is a reference to the created object. The lifetime of the created object extends as long as there is a reference to it[A].
The left hand side (LHS) creates a new local variable called cateringView. The reference returned by the RHS is stored in this variable. The lifetime of the created variable is a single iteration of the for loop.
At the end of the loop the code is:
[self addSubview: cateringView];
[cateringViews addObject: cateringView];
}
These two statements take the reference, to the created CateringView object, which is stored in the local variable cateringView and add it to this object's (which is an instance of the CateringPreviewCell class) subviews and cateringViews instance variable.
After these two statements have executed you have stored the reference to the created CateringView object three times: in the local variable cateringView, in the owning object's subviews, and in the owning object's cateringViews instance variable.
Also after these statements the loop iteration ends, so the lifetime of the local variable cateringView ends and you can no longer use that variable. However the reference to the object that was stored in that local variable still exists in two locations and that object is still alive.
You are stating you need to access cateringView.status outside of the loop. That does not make sense, the variable does not exist. However the object the variable referenced when it did exist is still alive, so the status value you seek is still around - you are just looking in the wrong place.
After the loop, and after the call to dataPrepared has returned, all the CateringView objects created can be accessed either:
as subviews of the object instance of CateringPreviewCell that dataPrepared was called on, or
as members of the instance variable cateringViews of that object instance.
The first of these is accessible "outside the class", the second can be provided you have instance methods defined on CateringPreviewCell which provided access to the instance variable.
HTH
[A]: This is not exactly true, but sufficient for the purpose here. Later you may learn about things such as weak references which do not govern how long an object lives.
You wouldn't use cateringView, because you actually have many of them. They're all stored in cateringViews so that's what you'd actually use. You'd either iterate all the views in that array or you'd choose one at a specific index to interact with.
Without knowing whether your intention is to try to add .status to
existing objects inside the cateringViews array
or
new objects that you add to cateringViews array
since your question has missing information and isn't very clear, here is the solution for #2
for (CateringView* cateringView in self.cateringViews) { //needed curly braces and self. to access property var
[cateringView removeFromSuperview];
}
[self.cateringViews removeAllObjects]; //again needed self.
for (int i = 0; i < food.catering.count; i++) { //make sure food.catering is array and not nil
CateringView* cateringView = [[CateringView alloc] init];
[cateringView.imageView loadURL:[NSURL URLWithString:item.image]
session:[DataManager sharedInstance].session
completion:nil];
FoodItem *item = food.catering[i]
cateringView.status = item.status;
[self addSubview: cateringView];
[self.cateringViews addObject: cateringView];
}
If I'm wrong and you're trying to accomplish #1, (consider improving your question wording if this is the case) then you'll need to loop through self.cateringViews as well.

How to replicate NSArray memory semantics in a subclass

Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.

Why does initial assignment to nil only happen once for a local static variable in a class method?

+ (NSArray *)motivations {
static NSArray *motivations = nil;
if (!motivations) {
motivations = [[NSArray alloc] initWithObjects:#"Greed", #"Revenge", #"Bloodlust", #"Nihilism", #"Insanity", nil];
}
return motivations;
}
The above code is from 'Learn Cocoa on the mac'. The book states that the initial assignment to nil only occurs the first time the method is called? My question is how/why is that the case?
Because statics are initialised only once. Despite the fact that the variable is inside the function, it's storage duration is that of the entire program. It's initialised once and maintains its value in between invocations of the function.
That code you posted is exactly the same conceptually as:
NSArray *motivations = nil;
+ (NSArray *)motivations {
if (!motivations) {
motivations = [[NSArray alloc] initWithObjects:#"Greed", #"Revenge",
#"Bloodlust", #"Nihilism", #"Insanity", nil];
}
return motivations;
}
in terms of the storage duration (though not the scope since motivations is now visible from outside). What you have here is a kind of singleton pattern which initialises the array to nothing, then populates it once the first time you use this code.
The ISO C99 standard (yes, I do realise this isn't C but the concepts are the same) states:
All objects with static storage duration shall be initialized (set to their initial values) before program startup.

My NSMutableArray doesn't work

sorry for my stupid question (beginner)
I got the demo program Accelerometergraph the apple site and would like to use
NSMutableArray in the values of acceleration x.
but my NSMutableArray contains only one object, there being several passage
NSMutableArray routine and should contain the same number of objects that the counter
show, how code below
if(!isPaused)
{
array = [[NSMutableArray alloc] init];
[filter addAcceleration:acceleration];
[unfiltered addX:acceleration.x y:acceleration.y z:acceleration.z];
NSNumber *number = [[NSNumber alloc] initWithDouble:acceleration.x];
[array addObject:number];
++a;
if (a == 30) // only check the # objs of mutablearray
{
sleep(2);
}
[filtered addX:filter.x y:filter.y z:filter.z];
}
It looks like you're missing a loop of some kind. The code you list above:
Makes sure something isn't paused.
Creates a new (empty) mutable array.
Adds a value to the new array.
And does some other work.
My guess is that this whole if{} block sits inside some kind of loop. You need to alloc and init the mutable array outside of the loop instead.
You create a new array each time the if block is entered, so the addObject: will only add the object to the most recently created array.
Furthermore, you are leaking the array and number objects. Each time you allocate an object, you are responsible for releasing it. Make sure you're familiar with the guidelines set out in the memory management programming guide.

Objective-C - How to implement an array of pointers in a method declaration

Ok, if you take a look at my two previous posts (Link #2 in particular), I would like to ask an additional question pertaining to the same code. In a method declaration, I am wanting to define one of the parameters as a pointer to an array of pointers, which point to feat_data. I'm sort of at a loss of where to go and what to do except to put (NSMutableArray*)featDataArray in the declaration like below and access each object via another pointer of feat_data type. BTW, sorry to be asking so many questions. I can't find some of the things like this in the book I am using or maybe I'm looking in the wrong place?
-(void)someName:(NSMutableArray*)featDataArray;
feat_data *featDataPtr = [[feat_data alloc] init];
featDataPtr = [featDataArray objectAtIndex:0];
Link #1
Link #2
Your declaration looks fine. "NSMutableArray*" is an appropriate type for your parameter. (Objective-C doesn't have generics so you can't declare anything about what's inside the array.)
One problem I see in your code is that you allocate an object for no reason and then throw away the pointer (thus leaking memory).
I don't know what it is that you are trying to do, so here are some things that you can do with an NSMutableArray:
- (void)someName:(NSMutableArray *)featDataArray {
feat_data *featDataPtr = [[feat_data alloc] init];
[featDataArray addObject:featDataPtr]; // add an object to the end
[featDataPtr release];
feat_data *featDataPtr2 = [[feat_data alloc] init];
[featDataArray replaceObjectAtIndex:0 withObject:featDataPtr2]; // replace an existing entry
[featDataPtr2 release];
feat_data *featDataPtr3 = [featDataArray objectAtIndex:0]; // get the element at a certain index
// do stuff with featDataPtr3
}