Method works correctly when called from within its class but not otherwise - objective-c

I wrote this function in my RootViewController. appRecord is an object holding an XML attribute.
- (NSString*) getString:(int)row{
AppRecord *appRecord = [self.entries objectAtIndex:row];
NSString *appName = appRecord.appName;
return appName;
}
I want to use this function again by writing this:
RootViewController *record = [RootViewController new];
NSString *appsName = [record getString:0];
NSLOG(appsName);
After I compiled, it didn't return anything, but it works and returns appsName if I use this function inside the RootViewController class ([self getString:0]), so I know there is no problem with the function. When I tried to change return appName to return #"test", it worked and returned something when I accessed it from the other class.
This means there is a problem with this appRecord; it was returned inside the class but returns nothing when I access it from another class.
How to solve this problem?

You are getting your AppRecord from self.entries in the instance of RootViewController record. This means unless your new initializer initializes and populates its entries variable (array by the looks of things) before you call getString:, it won't be able to return anything since that array is empty.
Beyond that, I don't know if you want your getString:(int) method to access a different array, or if the problem is in your initialization of RootViewController (more likely)

Maybe I don't understand your question, but if you are relying on the NSLog statement to see if the function is returning anything, you should change it to:
NSLog(#"%#", appsname);

Related

Initializing a constant that's value takes a completionBlock argument in its initializer

I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.

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.

Understanding Objective-C method value passing

Lets say I have in viewDidLoad:
NSMutableArray *entries = [NSMutableArray array];
[self doSomethingWithArray:entries];
NSLog(#"%#", entries);
Then in method I have:
- (void)doSomethingWithArray:(NSMutableArray *)entries
{
// create some custom data here, lets say - Something *something...
[entries addObject:something];
}
How is it possible that entries (one at the top) now (after method is finished) contain object something, since object "something" is not added to property or instance variable, and nslog will log class "Something" ? And doSomethingWithArray doesn't return anything since its "void".
I have encountered this for first time and dunno if there is any name of this appearance ?
I have seen this for second time in some examples and really dunno how its done.
If anyone could explain this a bit whats happening here I would be very very grateful.
Thank you a lot.
Because Objective-C instances are passed by reference (as you can tell by the * pointer syntax). You basically pass the address of the array to the doSomethingWithArray: method. In that method you add something to the array referenced by that address. And of course once the method returns, your array will contain that new object.
When you are adding the something object to the array, the array always retains it i.e it maintains copy of the Something object.
So NSLog prints the something.
Hope that helps.

Can't add to an NSMutableArray with addObject method

I am having an issue with the addObject method of an NSMutableArrayObject. Here's the code I'm using right now:
- (void)addBirdSightingWithName:(NSString *)name location:(NSString *)location {
BirdSighting *bird;
NSDate *today = [NSDate date];
bird = [[BirdSighting alloc] initWithName:name location:location date:today];
[self.masterBirdSightingList addObject:bird];
NSLog(#"Elements: %d", [self.masterBirdSightingList count]);
}
When this code runs, the NSLog call prints the value 0 to the console. I don't know what could be causing this.
EDIT:
I have looked deeper into the code, and I have discovered that the problem is that my BirdSightingDataController is never initialized. Now my question is: Where can I place the init for my BirdSightingDataController? In the viewDidLoad?
Thanks to everyone for the help.
Did you allocate memory to masterBirdSightingList?
self.masterBirdSightingList = [[NSMutableArray alloc] init];
In almost every case where cellForRowAtIndexPath: is not called is because numberOfRowsInSection: returns 0.
Place a log there and make sure you return more than one item and you should be able to see your cells. If you need further help please post the code in your:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Check your property accessor. Are you sure it's returning the correct object? Make sure the name of the property matches the instance variable or you've specified it correctly (For example: #synthesize masterBirdSightingList = _masterBirdSightingList;. If the property accessor doesn't match the iVar, it will return nil. Of course, if you're manually implementing the accessor check your code there. If you're not, you could also try manually implementing it to make sure.
To do a quick check, remove the self.masterBirdSightingList and replace it with masterBirdSightingList (assuming that's the iVar name) to access the iVar directly and see what happens.

Objective-C subclass and base class casting

I'm going to create a base class that implements very similar functions for all of the subclasses. This was answered in a different question. But what I need to know now is if/how I can cast various functions (in the base class) to return the subclass object. This is both for a given function but also a function call in it.
(I'm working with CoreData by the way)
As a function within the base class (this is from a class that is going to become my subclass)
+(Structure *)fetchStructureByID:(NSNumber *)structureID inContext:(NSManagedObjectContext *)managedObjectContext {...}
And as a function call within a given function:
Structure *newStructure = [Structure fetchStructureByID:[currentDictionary objectForKey:#"myId"]];
inContext:managedObjectContext];
Structure is one of my subclasses, so I need to rewrite both of these so that they are "generic" and can be applied to other subclasses (whoever is calling the function).
How do I do that?
Update: I just realized that in the second part there are actually two issues. You can't change [Structure fetch...] to [self fetch...] because it is a class method, not an instance method. How do I get around that too?
If I understand your question correctly I believe the key is the [self class] idiom.
As far as your update goes requesting a way to call a class method on the current class you can use [self class]. As in:
Structure *newStructure = [[self class] fetchStructureByID:[currentDictionary
objectForKey:#"myId"]];
inContext:managedObjectContext];
EDIT: I redid this to return id per #rpetrich's comment -- much cleaner and avoids the need for -isKindOfClass: as long as you're sure of the type of the instance you're calling -createConfiguredObject on.
As for the first part, you could just return an id (pointer to any object) and document that it will return an instance of the same class it's called upon. Then in the code you need to use [self class] anywhere you instantiate a new object in a method.
e.g. if you have a -createConfiguredObject method which returns an instance of the same class it's called on, it would be implemented as follows:
// Returns an instance of the same class as the instance it was called on.
// This is true even if the method was declared in a base class.
-(id) createConfiguredObject {
Structure *newObject = [[[self class] alloc] init];
// When this method is called on a subclass newObject is actually
// an instance of that subclass
// Configure newObject
return newObject;
}
You can then use this in code as follows:
StructureSubclass *subclass = [[[StructureSubclass alloc] init] autorelease];
subclass.name = #"subclass";
// No need to cast or use isKindOfClass: here because returned object is of type id
// and documented to return instance of the same type.
StructureSubclass *configuredSubclass = [[subclass createConfiguredObject] autorelease];
configuredSubclass.name = #"configuredSubclass";
For reference, what I was referring to with -isKindOfClass: and casting to the proper subclass is as follows:
Structure *structure;
// Do stuff
// I believe structure is now pointing to an object of type StructureSubclass
// and I want to call a method only present on StructureSubclass.
if ([structure isKindOfClass:[StrucutreSubclass class]]) {
// It is indeed of type StructureSubclass (or a subclass of same)
// so cast the pointer to StructureSubclass *
StructureSubclass *subclass = (StructureSubclass *)structure;
// the name property is only available on StructureSubclass.
subclass.name = #"myname";
} else {
NSLog(#"structure was not an instance of StructureSubclass when it was expected it would be.");
// Handle error
}