I recently added threading to an app so that network requests are not blocking the UI. On doing this, I discovered that I could no longer set my instance variable the same way as I had before implementing threading. My instance variable is a property declared as follows:
#property (nonatomic, strong) NSMutableArray *currentTopPlaces;
Here is how I incorrectly set my instance variable self.currentTopPlaces:
dispatch_queue_t downloadQueue = dispatch_queue_create("Flickr Top Places Downloader", NULL);
dispatch_async(downloadQueue, ^{
__block NSArray *topPlaces = [FlickrFetcher topPlaces];
dispatch_async(dispatch_get_main_queue(), ^{
self.tableRowCount = [topPlaces count];
[[self currentTopPlaces] setArray:topPlaces];
});
Using [self currentTopPlace] setArray:topPlaces] worked fine in the blocking version, before I started using GCD.
Now, I must set it like so for things to work correctly:
dispatch_queue_t downloadQueue = dispatch_queue_create("Flickr Top Places Downloader", NULL);
dispatch_async(downloadQueue, ^{
__block NSArray *topPlaces = [FlickrFetcher topPlaces];
dispatch_async(dispatch_get_main_queue(), ^{
self.tableRowCount = [topPlaces count];
self.currentTopPlaces = topPlaces;
});
Can someone explain to me the difference between using:
[[self currentTopPlaces] setArray:topPlaces];
and:
self.currentTopPlaces = topPlaces;
Specifically, why the "setArray" call did not work in a threaded block?
I thought dot notation in Objective-C is syntactic sugar and not mandatory. I would like to know the "non-sugared" way to achieve the same behavior.
[self currentTopPlaces] and self.currentTopPlaces are in fact identical, but
[self.currentTopPlaces setArray:topPlaces]; // (1)
self.currentTopPlaces = topPlaces; // (2)
are not. (1) replaces all elements of self.currentTopPlaces with those from topPlaces. (2) assigns a new value to self.currentTopPlaces (releasing the old value if it was not nil).
A difference occurs if self.currentTopPlaces is nil: (1) does nothing, because the setArray: method is sent to nil. (2) assigns a new value to self.currentTopPlaces.
Btw: The __block modifier is not necessary in your code, because the block will not change the value of topPlaces.
[[self currentTopPlaces] setArray:topPlaces];
self.currentTopPlaces = topPlaces;
These are two entirely different expressions. The first is as written, the second would be:
[self setCurrentTopPlaces:topPlaces];
If you want to do the first one with dot notation, it would be:
self.currentTopPlaces.array = topPlaces;
Related
Hi I am trying to use queues and blocks to Multithread but I am having troubles accessing properties from these blocks. When I use NSLog to print the value of self.photoArray I get null. Can someone explain what I did wrong? I have tried This
dispatch_queue_t downloadQueue = dispatch_queue_create("flickrDownload", NULL);
dispatch_async(downloadQueue, ^{
NSArray* photos = [FlickrPhotoSort sortTopPlaces];
dispatch_async(dispatch_get_main_queue(), ^{
self.photoArray = photos; });
});
dispatch_release(downloadQueue);
and this which doesnt use main queue.
dispatch_queue_t downloadQueue = dispatch_queue_create("flickrDownload", NULL);
dispatch_async(downloadQueue, ^{
NSArray* photos = [FlickrPhotoSort sortTopPlaces];
self.photoArray = photos;
});
dispatch_release(downloadQueue);
Here is the setter of photoArray
-(void) setPhotoArray:(NSArray *)photoArray{
if(!_photoArray) _photoArray = [[NSArray alloc] initWithArray:photoArray];
else
_photoArray = photoArray;
}
I am following the online stanford courses and the teacher simply called the property like this and it seems to work
dispatch_queue_t downloadQueue = dispatch_queue_create("flickr downloader", NULL);
dispatch_async(downloadQueue, ^{
NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];
dispatch_async(dispatch_get_main_queue(), ^{
self.navigationItem.rightBarButtonItem = sender;
self.photos = photos;
});
});
dispatch_release(downloadQueue);
When I use NSLog to print the value of self.photoArray I get null.
The asynchronously running [FlickrPhotoSort sortTopPlaces] is probably still running when you hit the log statement and the property just hasn't been set, yet.
Since properties invoke a method call I have had no issue setting them within a block. __block only adds visibility to a variable outside the scope of the block - so that changes to a variable from within the block are seen outside the block's scope. It has no effect on properties only on using vars directly in the block (where they be ivars or not.) properties are not ivars they are explicit method calls to a class which sets an ivar outside the scope of the block.
You should be retaining the photo array in the setter though - the array will be released at the end of the block otherwise. I have tons of code setting properties from within blocks with no issues.
Use the keyword __block for iVars in blocks. This makes sure, that the property stays in memory. If you use ARC and pre 5.0 use __unsafe_unretained else __weak
__block NSArray *bPhotoArray = self.bPhotoArray;
dispatch_async(downloadQueue, ^{
NSArray* photos = [FlickrPhotoSort sortTopPlaces];
bPhotoArray = photos;
});
At the moment I explore blocks opportunities, I read more then ten times apples docs) but i cant understand blocks behaviour in blogs examples and in my code. I know about __block modifier. So please look at few my examples, and say why it work or not:
1)Not working. Need __block, because I want to modified object.And self retained(correct?) and in blogs I'm frightened by retain cycle of self. Does it is?
NSDictionary *result = nil;
dispatch_async(queue, ^{
result = [self sendRequest:apiRequest];
});
2)Not working. I dont understand about variable scope of dispatch_async(dispatch_get_main_queue(), ^{});. Does it see all local variables in main thread or it is separate block from main thread, but exicuted in main thread? Like dispatch_async(dispatch_get_main_queue()^{}); copies all local variables in dispatch_async(queue,^{}); and switches semaphore to main thread.
NSDictionary *result = nil;
dispatch_async(queue, ^{
NSDictionary *data = [self sendRequest:apiRequest];
dispatch_async(dispatch_get_main_queue(), ^{
result=[data retain];
});
});
3)And examples from blogs and stanford's course that particularly muddle me, because they work.
- (void)viewWillAppear:(BOOL)animated
{
dispatch_queue_t downloadQueue = dispatch_queue_create(“image downloader”, NULL);
dispatch_async(downloadQueue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:networkURL];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
self.imageView.image = image;
self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
 self.scrollView.contentSize = image.size;
});
});
dispatch_release(downloadQueue);
}
I dont understand, because first they dont mention about __block for self in the articles and the lessons, second this code modified variables, but through properties and complier doesn't swear that variables arent assignable and properties change reference, not value. Final it works. Thanks in advance.
Old question, but I feel like new readers might benefit from a more precise answer than the current one:
1) result is a pointer to an object, not an object itself. Prepending __block is necessary because that pointer is assigned to within the block. Modifying objects pointed to by pointer variables outside of the block is not the problem, e.g.
NSMutableDictionary* result = [NSMutableDictionary new];
and then modifying the object within the block via [result setObject...] would be fine. And that's the reason why 3) works: Only variables of the object pointed to by self are being modified. The pointer self is never assigned to.
Regarding retainment under ARC of the objects referenced by the block, see e.g.:
ARC with blocks and retain cycles
ARC, Blocks and Retain Cycles
blocks and ARC - copy or crash with release build (caused by optimization level)
1) Yes you'd need to declare:
__block NSDictionary *results = nil;
If you're using ARC it should automatically take care of retaining and releasing the results object. If not using ARC, then do a retain as the first thing you do and a release as the last thing you do in the block.
2) A block should have visibility of all variables/objects available to it's parent's scope. So in a method you should be able to see all local variables to that method and the object which that method belongs to (as self).
3) Not too sure about this one. Variables do go away once you leave the function/method (which will be as so as most blocks have been called, rather than finished executing), where as anything that is part of self won't. Maybe that has something to do with it.
I'd like to add search functionality to a TableView in my app. I populate a table with an NSArray which has x amount of Objects that contain 3 NSStrings. Here's how I construct that NSArray:
First I create a class Code.h:
#import <Foundation/Foundation.h>
#interface Code : NSObject
#property (nonatomic, strong) NSString *codeName;
#property (nonatomic, strong) NSString *codeNumber;
#property (nonatomic, strong) NSString *codeDesc;
#end
Next, I synthesize these NSStrings in Code.m.
Now in my SearchViewController.m, Here's how I create my dataset:
NSMutableArray *codes;
codes = [[NSMutableArray alloc] init];
Code *c = [[Code alloc] init];
[c setCodeNumber:#"1"];
[c setCodeName:#"First Title Here"];
[c setCodeDesc:#"I might write a desc in here."];
[codes addObject:c];
c = [[Code alloc] init];
[c setCodeNumber:#"2"];
[c setCodeName:#"Second Title Here"];
[c setCodeDesc:#"2nd desc would be written here."];
[codes addObject:c];
and so on...
Here is how I display it: cellForRowAtIndexPath:
Code *c = [codes objectAtIndex:indexPath.row];
NSString *fused = [NSString stringWithFormat:#"%# - %#",[c codeNumber],[c codeName]];
cell.textLabel.text = fused;
return cell;
So now that you know how my data is structured and displayed, do you have an idea of how to search either the NSArray or possibly (preferably) the TableCells that have already been created?
I have been through the few tutorials online regarding Adding a Search Bar to a TableView, but all of them are written for using arrays setup using simple arrayWithObjects.
SIDETHOUGHT: Is it possible for me to construct an arrayWithObjects:#"aaa-1",#"bbb-2",#"ccc-3"... from my data? If i can manage that, I can use those tutorials to populate my cells and search them!
UPDATE:
Your second answer makes plenty more sense to me! Thanks for that. I beleive I have followed your instruction, but I am getting a "-[Code search:]: unrecognized selector sent to instance 0x6a2eb20` when that line is hit.
I added #property (nonatomic, strong) NSString *searchString; to Code.h and synthesized it in Code.m
I added NSMutableSet *searchResults; to SearchViewController.h's #interface
I added your methods performSearchWithString and matchFound to SearchViewController.m
Directly under those I added this to call performSearchWithString
x
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchString {
NSLog(#"%#",searchString); //Just making sure searchString is set
[self performSearchWithString:searchString];
[self.tableView reloadData];
}
The error hits when [codes makeObjectsPerformSelector:#selector(search:) withObject:self]; runs. I am confused b/c it sounds like Code doesn't recognize searchString, but I know I added it in Code.h.
UPDATE:
In order to store objects in searchResults, I had to change searchResults from a NSMutableSet to a NSMutableArray and modify - (void)matchFound:(Code *) matchingCode {} to this:
-(void) matchFound:(Code *) matchingCode {
Code *match = [[Code alloc] init];
if (searchResults.count == 0) {
searchResults = [[NSMutableArray alloc] init];
[match setCodeName:[matchingCode codeName]];
[match setCodeNumber:[matchingCode codeNumber]];
[match setCodeDesc:[matchingCode codeDesc]];
[searchResults addObject:match];
}
else
{
match = [[Code alloc] init];
[match setCodeName:[matchingCode codeName]];
[match setCodeNumber:[matchingCode codeNumber]];
[match setCodeDesc:[matchingCode codeDesc]];
[searchResults addObject:match];
}
With a few other tweeks, I've got a working searchbar for my tableView. Thanks Tim Kemp!
Oh, also case insensitive search was what I was looking for. NSRange rangeName = [codeName rangeOfString: searchString options:NSCaseInsensitiveSearch];
I hope this question and answer will be helpful to the next developer learning objective-c with this question!
Simpler approach
You asked for a simpler solution. This one isn't nearly as flexible, but it will achieve the same things as my earlier answer for this specific case.
Once again we are going to ask Code to search its strings for us. This time, we are going to skip the SearchRequest and the block callback and implement it directly.
In your SearchViewController you will create two methods. One to do the search, and one callback to process any results as they come back. You will also need a container to store matching Code objects (more than one might match, presumably.) You will also need to add a method to Code to tell it what the search string is.
Add an ivar NSMutableSet called searchResults to SearchViewController.
Add a property of type NSString * called searchString to Code
Add the search method to SearchViewController. This is what you'll call when you want to initiate a search across all your codes:
-(void) performSearchWithString:(NSString *) searchString {
// Tell each Code what string to search for
[codes makeObjectsPerformSelector:#selector(setSearchString:) withObject:searchString];
// Make each code perform the search
[codes makeObjectsPerformSelector:#selector(search:) withObject:self];
}
Then you will also need a callback in SearchViewController. This is so that your Code objects can tell the SearchViewController that they have found a match:
-(void) matchFound:(Code *) matchingCode {
[searchResults addObject:matchingCode];
// do something with the matching code. Add it to a different table
// view, or filter it or whatever you need it to do.
}
However do note that you don't have to use the searchResults mutable set; you may well want to just call another method to immediately add the returned result to some other list on screen. It depends on your app's needs.
In Code, add a search method a bit like we had before, but instead of the SearchRequest parameter we'll pass in a reference to the SearchViewController:
- (void) search:(SearchViewController *) searchVC {
// Search each string in turn
NSRange rangeNum = [codeNumber rangeOfString : searchString];
NSRange rangeName = [codeName rangeOfString : searchString];
NSRange rangeDesc = [codeDesc rangeOfString: searchString];
if (rangeNum.location != NSNotFound || rangeName.location != NSNotFound || rangeDesc.location != NSNotFound) {
[searchVC matchFound:self];
}
}
Do you see how that works? If there's a match in any of the strings (|| means 'or') then pass self (which means exactly what it sounds like: the current object that's running this code right now) back to a method in the view controller called searchVC. This is called a callback because we are "calling back" to the object which originally sent us the message to do the search. We have to use callbacks rather than simple return types because we have used makeObjectsPerformSelector to tell every single Code in the codes array to do a search. We never explicitly called the search method ourselves, so we have no way to capture the return value from each search. That's why its return type is void.
You can extend matchFound to take an additional parameter which identifies which string the match was in (i.e. çodeNumber, codeName or codeDesc.) Look into enums as one good approach to pass around that kind of data.
Hope that's bit simpler.
Here is a link to an excellent language introduction/tutorial which will eliminate much confusion.
EDIT In your last comment you said that searchResults was null. I said to add it as an ivar somewhere in SearchViewController. In your initialiser method for SearchViewController you should call
searchResults = [[NSMutableSet alloc] initWithCapacity:50]` // Choose some sensible number other than 50; enough to hold the likely number of matching Code objects.
Alternatively you could 'lazy initialise' it in matchFound:
- (void) matchFound:(Code *) matchingCode {
if (!searchResults)
searchResults = [[NSMutableSet alloc] initWithCapacity:50];
[searchResults addObject:matchingCode];
}
Though if you do this you should be aware that anywhere else you access searchResults may find that it's null if matchCode: has never previously been called.
Original, flexible and more complicated answer
I'm a little unclear as to what you're trying to do, so I'm going with your title, "Searching each string in each object of an array." In your case, your Codes have three strings and your array has multiple Codes. I assume that you need a way to tell the caller - the code that wants to do the search - which Code matches.
Here is one approach. There are easier ways but this technique is quite flexible. Broadly, we are going to make the Code object do the work of searching its own strings. We are then going to give the Code object the ability to tell the caller (i.e. the object that owns the codes array, presumably your table view controller) whether any of its strings match the search string. We will then use NSArray's method makeObjectsPerformSelector to have to tell all of its Code objects to search themselves. We will use a block for a callback.
Firstly, add a search method to Code (in the interface, or as a category depending on your design), something like this:
-(void) search:(SearchRequest *) request {
// Search using your favourite algorithm
// eg bool matches = [searchMe [request searchString]];
if (matches) {
[request foundMatch:self];
}
}
SearchRequest is new. It's a place to tie together a search string and a callback block. It looks something like this:
#interface SearchRequest
#property (retain) NSString * searchString;
#property (copy) void (^callback)(Code *);
- (id) initWithSearchString:(NSString *) search callback:(void (^)(Code *)) callback;
- (void) foundMatch:(Code *) matchingCode;
#end
#implementation SearchRequest
// synthesize...
// initialiser sets ivars
- (void) foundMatch:(Code *) matchingCode {
callback(matchingCode);
}
The callback block is our way of communicating back to the caller.
When you want to perform a search, construct a SeachRequest object with the string you're searching for and a block which contains the method to call when you get a match.
That would look like this, in the caller:
- (void) performASearchWithString:(NSString *) searchForMe {
SearchRequest * req = [[SearchRequest alloc] initWithSearchString:searchForMe
callback:^(Code * matchingCode) {
[self foundAHit:matchingCode];
}];
[codes makeObjectsPerformSelector:#selector(search:) withObject:req];
}
You then need to implement foundAHit in your caller, which takes the matching Code and does something with it. (You don't have to use a block: you could store a reference to the caller and a selector to call on it instead. I won't go into the arguments for either case here. Other answerers can propose alternatives.)
I am trying to create a set of objects using NSMutableSet. The object is a tag, each tag has an id and a name.
The tag class is defined like so:
#import "Tag.h"
#implementation Tag
#synthesize id, name;
+(id) populateTagObjectWithId:(NSString *)id andName:(NSString *)name
{
Tag *myTag = [[self alloc] init];
myTag.id = id;
myTag.name = name;
return myTag;
}
... remainder of code snipped out
Somewhere else in my application I use SQLite to fetch tags in the TAG table. I iterate using a while loop, for each iteration I build a tag object and then try to add it to the set. Code below:
... previous code snipped out...
NSMutableSet *thisTagSet;
while(sqlite3_step(tag_statement) == SQLITE_ROW)
{
NSString *thisTagId = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(tag_statement, 0)];
NSString *thisTagName = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(tag_statement, 1)];
[thisTagSet addObject:[Tag populateTagObjectWithId:thisTagId andName:thisTagName]];
... rest of code snipped out...
So, as I mentioned, as I iterate through this while loop, i get the object and its id and name ARE populating (I have confirmed this by inspecting the debugger and also using NSLog). The thisTagSet NSMutableSet however remains empty even though I'm using the addObject method. Is there something I'm doing wrong here? I have also tried separating out the two steps like so:
Tag *thisTagObject = [Tag populateTagObjectWithId:thisTagId andName:thisTagName];
[thisTagSet addObject:thisTagObject];
Again, the same result. I successfully get a thisTagObject, but nothing in the thisTagSet...
After reading your code two things pop out:
You're not initializing your NSMutableSet, and you're leaking your tags by returning a retained object in your class method.
Edit: Added leak-fix code
+(id)tagObjectWithId:(NSString *)id andName:(NSString *)name
{
Tag *myTag = [[self alloc] init];
myTag.id = id;
myTag.name = name;
return [myTag autorelease];
}
2nd edit: The code above only applies as long as ARC is disabled. Otherwise it is not needed as ARC takes care of the memory management.
I've just run into blocks and I think they are just what I'm looking for, except for one thing: is it possible to call a method [self methodName] from within a block?
This is what I'm trying to do:
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
void (^tempFunction)(void) = ^ {
[self changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
}
I've been searching for a couple of days and I can't find any evidence that this is possible.
Is this at all possible, or am I trying to use blocks for something they aren't meant for?
The reason I'm using blocks is that I've created a Fader class, and I want to store a block for it to execute when it finishes fading out.
Thank you
EDIT:
Okay, I added in the suggestion, but I'm still getting an EXC_BAD_ACCESS error...
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
__block MyScreen* me = self;
void (^tempFunction)(void) = ^ {
[me changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
[fader release];
}
Maybe I'm not allowed to give fader the function...?
Yes, you can do this.
Note, however, that the block will retain self. If you end up storing this block in an ivar, you could easily create a retain cycle, which means neither would ever get deallocated.
To get around this, you can do:
- (void) someMethodWithAParameter:(id)aParameter {
__block MySelfType *blocksafeSelf = self;
void (^tempFunction)(void) = ^ {
[blocksafeSelf changeWindow:game];
};
[self doSomethingWithBlock:tempFunction];
}
The __block keyword means (among other things) that the referenced object will not be retained.
The accepted answer is outdated. Using __block in that case can cause errors!
To avoid this problem, it’s best practice to capture a weak reference to self, like this:
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}
Please, look at Apple Documentation - Avoid Strong Reference Cycles when Capturing self
for more details.
__block CURRENTViewController *blocksafeSelf = self;
[homeHelper setRestAsCheckIn:strRestId :^(NSObject *temp) {
[blocksafeSelf YOURMETHOD:params];
}];
Is it possible to call a method [self methodName] from within a block?
Yes, why not. If your tempFunction is an instance method, you can do it. The called method should be accessible is the only restriction.
Consider this (which I think is the best practice)
#implementaion ViewController
- (void) viewDidLoad {
__weak typeof(self) wself = self;
[xxx doSomethingUsingBlock: ^{
__strong typeof(wself) self = wself;
[self anotherMessage];
}];
}
#end
Moreover, You can define wrapper macros.
#define MakeWeakSelf __weak typeof(self) wself = self
#define MakeStrongSelf __strong typeof(wself) self = wself
I wonder whether you [fader setFunction:tempFunction]; then is synchronous or asynchronous.
blocks push onto stack.so in MRR,if you don't retain it,it will pop off.
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
void (^tempFunction)(void) = ^ {
[self changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
//if the tempFunction execute there will be right.
}//there the tempFunction pop off
//....some thing go on
//execute the tempFunction will go wrong.