How to pass data (arrays) between functions - objective-c

I must use s7graphview library for draw simple histogram, and I've got custom function called
-(IBAction)histogram:(id)sender;. in this function every pixel from image is passed to array as RGB representation. then pixels are counted and I've got red, green and blue array. I can send to NSLog or something but problem is, when I try to send 3 arrays to - (NSArray *)graphView:(S7GraphView *)graphView yValuesForPlot:(NSUInteger)plotIndex;. both functions are in the same .m file, and I have no idea how to pass data between them, because when I write redArray, Xcode don't suggest me this name.

Since - (NSArray *)graphView:(S7GraphView *)graphView yValuesForPlot:(NSUInteger)plotIndex is a delegate method, it should be implemented in your class that is posing as a delegate to S7GraphView object. You don't call explicitly, you define it as such in your .m implementation:
- (NSArray *)graphView:(S7GraphView *)graphView yValuesForPlot:(NSUInteger)plotIndex
{
if ( plotIndex == <some index value> )
return redArray;
else
return nil;
}
I have no idea what plotIndex corresponds with your various color arrays, but you should get the idea.
When the S7GraphView object needs that data, it will invoke that delegate method.
This is not unlike implementing UITableViewDelegate and UITableViewDataSource methods. When a UITableView method -reloadData is invoked, it will call upon your view controller (presuming it is delegate/data source of the table) to supply UITableViewCell objects via
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = <... dequeue or created ... >.
/*
do some cell set up code based on indexPath.section and indexPath.row
*/
return cell;
}
Similar with S7GraphView I'm sure (I don't have the API to see all it does). In your IBAction method, you will probably be doing something like:
- (IBAction)histogram:(id)sender
{
// maybe you recalculate your red, green, and blue component arrays here and cache
// or maybe you calculate them when requested by the delegate method
// tell the S7GraphView it needs to update
// (not sure what the reload method is actually called)
[self.myS7GraphView reloadGraph];
}

Related

Why use Message Forwarding

Why would you use Message Forwarding in Objective-c. Also when using it, you need to call the Surragate method with performSelector? I am thinking you have a lot of knowledge when you are coding this, why would you need Message Forwarding. I am reading that this is Obj-c multiple inheritance - but I don't see it like this, it is just another object attached to an object.
Hope you know and have a pratical example
Thanks
See my discussion at the end of this section: http://www.apeth.com/iOSBook/ch25.html#_uitabbar
Here I have interposed my own object as the data source for one of Apple's table views. I don't know how the table view works (it's Apple's) and I don't know what their data source does (it's Apple's). So I substitute my own data source but I keep Apple's, and any messages that I don't handle that arrive at my data source are passed on to Apple's. Thus in a sense I have made my data source "inherit" from Apple's.
#implementation MyDataSource
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.originalDataSource respondsToSelector: aSelector])
return self.originalDataSource;
return [super forwardingTargetForSelector:aSelector];
}
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)sec {
// this is just to quiet the compiler
return [self.originalDataSource tableView:tv numberOfRowsInSection:sec];
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)ip {
// this is why we are here: my tweaks
UITableViewCell* cell = [self.originalDataSource tableView:tv cellForRowAtIndexPath:ip];
cell.textLabel.font = [UIFont fontWithName:#"GillSans-Bold" size:14];
return cell;
}
#end

Observe changes of instance variable in Objective-c

I have a UITableView with a property(strong, nonatomic)NSMutableArray *currentContent, I also have a property(strong, nonatomic)NSMutableArray *cellHeights to keep track of the cell heights cos the user could expand or collapse each cell. The self.currentContent is set by another controller which load data from a web service, so it will change as the data load, I want to keep both of these variables in sync. As soon as currentContent is updated, I want to update cellHeights. How do I do that?
I tried:
- (void)setCurrentContent:(NSMutableArray *)currentContent{
_currentContent = currentContent;
self.cellHeights = [NSMutableArray arrayWithDefaultHeightsForCellCount:[currentContent count]];
}
But it's not working, cos it will only be set at the first time when I set currentContent, when it's empty. So self.cellHeights currently will stay empty. When there is finally value in self.currentContent, self.cellHeights was not updated.
I've done a similar thing before, with variable cell heights depending on the content from your web-service, and I'd advise that keeping an array of cell heights might not be the best idea.
What I did was to create a 'fake' cell in the viewDidLoad: method, that I use just to calculate cell heights.
Then I use the 'heightForRowAtIndexPath' method to specify how tall cell should be by populating the 'fake' cell with the data for the index path, then finding out how tall that cell is. For example:
#interface MyTableViewController()
#property (nonatomic, strong ) MyCustomTableViewCell *cellForTestingHeight;
#end
#implementation MyTableViewController
- (void)viewDidLoad {
[super viewDidLoad]
self.cellForTestingHeight = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *myData = [self.data objectAtIndex:indexPath.row];
self.cellForTestingHeight.viewData = myData;
return self.cellForTestingHeight.height;
}
#end
This code assumes that you've created a class called MyCustomTableViewCell which has a method to set the viewData property on it, and that after setting that property you'll be able to tell how tall that cell will be by accessing a height property.
What you need to do is observe changes to the mutable array itself, not your property which references the array. This should be straightforward but unfortunately there is a twist...
OK, let's assume there is no twist and rather than an NSMutableArray your property is of type MyObservableClass. Then to setup the observing you would do something like this (all code is pseudo - i.e. typed into the answer):
- (void)setCurrentContent:(MyObservableClass *)currentContent
{
if(_currentContent)
[_currentContent removeObserver:self forKeyPath:#"myObservableProperty"];
_currentContent = currentContent;
[_currentContent addObserver:self forKeyPath:#"myObservableProperty" ...];
}
Now whenever the currentContent is changed your code stops observing the properties of its old value and starts observing properties of its new value.
Easy, but its not quite that simple for arrays...
The twist, though KVO will inform an observer of additions, deletions and changes to a collection the NSMutableArray class doesn't issue those notifications. You need to use a proxy for the array, and such a proxy is provided by the frameworks - see for example NSArrayController and mutableArrayValueForKey:. However the second part of the twist is that the changes to the array must be done through this proxy - you can't have your client call setCurrentContent and pass any NSMutableArray if you are going to observe it, your client needs to pass in the proxy... Whether you can use this easily in your design you'll have to figure out. You'll need to read Automatic Change Notification in this document and other Apple docs to sort this out.
This sounds more complicated that it needs to be, maybe somebody else will provide a more succinct way of doing this.

UITableViewDataSource and Multithreading

I'm running into index beyond bounds exception in one of my UITableViews and I think it could be down to some multithreading issues. Here's what I believe is happening:
I have a UITableView and it's data source is a regular NSMutableArray.
This NSMutableArray which is backing my UITableView is updated every couple of seconds with the contents of an API response.
After each update, UITableView's reloadData is being invoked to ensure that the user sees new data from the API server.
Sometimes a index beyonds bounds exception gets thrown.
Here's my code:
-(NSMutableArray*) currentBetEvents
{
return currentMarketId == nil ? [[BFOpenBetsModel sharedInstance] betEvents] : filteredBetEvents;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSArray *betEvents = [self currentBetEvents];
return [betEvents count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *betEvents = [self currentBetEvents];
id obj = [betEvents objectAtIndex:indexPath.section] // this is where it blows up
Basically, I get an exception while trying to access an object in the betEvents structure at index 0.
What I believe is happening is:
reloadData is called on the UITableView
numberOfSectionsInTableView: is invoked which returns a value > 0.
a rouge thread arrives and clears out the UITableView's data source.
cellForRowAtIndexPath: is invoked and it bombs.
Is there any way to ensure that this doesn't happen? Do I need to start using some primitive locks on the data source to ensure that it doesn't get updated while the table is being updated?
EDIT
Took another look at how the data structures returned by currentBetEvents can be altered and it looks like the filteredBets & betEvents can be cleared out as a result of the following code:
[[NSNotificationCenter defaultCenter] postNotificationName:kUserLoggedOutNotification object:nil];
This notification is posted whenever the user logs out. Whenever a user logs out of the app, I need to clear out the filteredBets and betEvents arrays. Is it possible that the following could happen:
reloadData is called on the UITableView
numberOfSectionsInTableView: is invoked which returns a value > 0.
User logs out which kicks off the notification & clears out the data structures.
cellForRowAtIndexPath: is invoked and it bombs.
Thanks,
Sean
Definitely sounds like a threading problem. You might try something like this:
// view controller
#synchronized([[BFOpenBetsModel sharedInstance] betEvents])
{
[self.tableView reloadData];
}
…
// data model
#synchronized(_betEvents) // or whatever the instance variable -betEvents returns is
{
[_betEvents addObject:whatever];
}

Loading a UITableViewCell with a NSOperationQueue

Hi guys I have an UITableView that loads precomputed cells from NSMutableArray. I want to use NSOperationQueue or PerformSelectorOnMainThread to update the user interface to enable smooth scrolling but i get an error... this is my code...
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//queue is being initialized in viewDidLoad
[queue addOperationWithBlock:^ {
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
NSLog(#"Updating...");
return [self.CellObjects objectAtIndex:indexPath.row];
//if you remove the line above with the return, NSOperationQueue will work but I need the above line to load the cell.
}];
}];
}
Is there a way to make it work? Any help appreciated!
Why not simple ...
return [self.CellObjects objectAtIndex:indexPath.row];
...?
It's a mess. Why do you have two calls to addOperationWithBlock:? And also your return statement has nothing to do with return value of tableView:cellForRowAtIndexPath:. It's return value of your block, so, it will never work.
What's your error? I assume it's about incompatible block pointer, because it expects void(^)(void) and you're trying to send UITableViewCell *(^)(void).
Blocks are not gonna help you there. If you have precomputed cells in CellObjects, just use only the return self.CellObjects[indexPath.row]; line.
Also don't use property names like CellObjects. Should be named cellObjects. Check the case.
Offsetting your cell retrieval in that way is not going to give you any advantage. You need to compute your cell height/size/content prior to your UITableView instance asking for cells.
UITableView is expecting a UITableViewCell to be returned from that delegate callback on the
main thread.
A better idea would be to place the computation on other threads if they require time and their on completion you can call back to your UITableView to reloadData.

method syntax in objective-c [duplicate]

This question already has answers here:
Method Syntax in Objective-C
(4 answers)
Closed 3 years ago.
I'm a .NET programmer new to objective-c, and I'm struggling to understand some nuts and bolts syntax. For example, how should I parse this method signature:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
I understand what the "-" char means, and (UITableViewCell *) defines the return type. But the rest has me confused.
(1) (2) (3) (4) (5) (6) (7) (8)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
"-" Defines an instance method
Returns UITableViewCell pointer
First part of the method signature named "tableView"
Takes a UITableView pointer
With the local variable name "tableView"
Second part of the method signature "cellForRowAtIndexPath"
Takes a NSIndexPath pointer
With the local variable name "indexPath".
The actual method signature is: tableView:cellForRowAtIndexPath:.
Read Apple's documentation, like Objective-C: A Primer. It's explained right there. You know, the maker (Apple or Microsoft) has a lot of documentation on their site ...
Objective-C uses named, inline parameters for methods. (As bblum points out in the comment below, this style of parameters are sometimes called "interleaved".) This is a reflection of it's heratage as a mix of C and SmallTalk syntax. The trailing colons denote the names of the parameters to the method. For your method, the full name of the method is referred to as tableView:cellForRowAtIndexPath:. It takes two parameters, a pointer to a UITableView, and pointer to a NSIndexPath. In a java-like language, this method signature would look something like:
public UITableViewCell cellInTableViewForRowAtIndexPath(UITableView tableView, NSIndexPath indexPath);
Every foo:(bar)baz defines a parameter, for example
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ... {
defines a method with five* parameters.
The stuff before the : is part of the name of the method. In this example, the method's name is
initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:
The stuff between the (…) is the type of that argument. Here, we see that the first argument must be an NSString*.
Finally it's the name of the parameter.
(*: Sometimes there is sometimes a , ..., like in here, indicating it's a variadic method.)
The method is called in the syntax
id result = [theAllocedAlertView initWithTitle:#"title"
message:#"message"
delegate:someDelegate
cancelButtonTitle:#"cancel button title"
otherButtonTitles:#"other", #"button", #"titles", nil];
So the name of the method is repeated (in order!), and the parameter names are substituted by the actual arguments.
In C#, the corresponding function signature would look like
object InitWithTitleAndMessageAndDelegateAndCancelButtonTitleAndOtherButtonTitles(
string title,
string message,
object delegate,
string cancelButtonTitle,
params string[] otherButtonTitles);
and called like
object result = theAllocedAlertView.InitWithBlahBlahBlahAndOtherButtonTitles(
"title",
"message",
someDelegate,
"cancel button title",
"other", "button", "titles");
The method selector is:
tableView:cellForRowAtIndexPath:
where each value after the colon is a parameter. The signature is meant to read like an English sentence, i.e. "The TableView's cell for a row at this index".
If this were written in another language it might look like this
// #param (UITableView *) tableView
// #param (NSIndexPath*)indexPath
// #return UITableViewCell
- (UITableViewCell *) someFunctionName(tableView, indexPath) {
}
Thats roughly speaking of course. It would not be written like this in objective-c. However I believe it is possible to write a good chunk of your program in c++