I'm developing a app in Searching with sqlite database using ARC.
I have to retrieve data from database into NSMutableArray to show retrieved data when textDidChange Event of UISearchBar.
It's has alot of data retrieve from database when textDidChange of UISearchBar.
After retrieved alot of data from database,i have always clear from NSMutableArray with following codes.
[self.myArrayData removeAllObjects];
However i remove all of data from NSMutableArray , memory allocations are still occurring.
Not reduced.
Next time when i type something in UISearchBar, memory allocations are still increasing.
After i typed 10 times or above,I receive memory warning from XCodes and out of memory.
I want to know why data from NSMutableArray isn't clear when i called removeAllObjects method.
I am developing my app with ARC.
Please help me.
You don't have a lot of information here but it may not be NSMutableArray holding on to the data. SQLite also keeps a cache of recently retrieved records. You can reduce/remove the SQLCache by executing this after you've opened the database. Set the cache size to the number of pages you want SQLite to cache. The default is 2000. Each page is 1024 bytes in size.
const char *pragmaSql = "PRAGMA cache_size = 0";
if (sqlite3_exec(database, pragmaSql, NULL, NULL, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to execute pragma statement with message '%s'.", sqlite3_errmsg(database));
}
EDIT: Technically you can't reduce the cache below 10. If you try, the program will simply set the cache to 10.
if after [self.myArrayData removeAllObjects]; [self.myArrayData count] == 0 you can be sure that your array has removed its objects and decremented their retain counts... but if other objects still own them they they wont actually be dealloc'ed... so the objects may be over-retained and therefor leaked or they may just be owned by something else.
Related
I have a function that constructs an NSMutableDictionary using bk_apply, a method provided by the third-party block utility library BlocksKit. The function's test suite usually passes just fine, but once every couple runs it crashes.
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
[inputSet bk_apply:^(NSString *property) {
NSString *localValueName = propertyToLocalName[property];
NSObject *localValue = [self valueForKey:localValueName];
result[property] = localValue ?: defaults[property]; // Crash
// Convert all dates in result to ISO 8601 strings
if ([result[property] isKindOfClass:[NSDate class]]) { // Crash
result[property] = ((NSDate *)result[property]).ISODateString; // Crash
}
}];
The crash always happens on a line where result is referenced, but it's not the same line every time.
Examining the contents of result in the debugger, I've seen very strange values like
po result
{
val1 = "Some reasonable value";
val2 = "Also reasonable value";
(null) = (null);
}
It's impossible for an NSDictionary to have null keys or values, so clearly some invariant is being violated.
What is causing this crash and how do I fix it?
From the BlocksKit documentation for bk_apply:
Enumeration will occur on appropriate background queues. This will
have a noticeable speed increase, especially on dual-core devices, but
you must be aware of the thread safety of the objects you message
from within the block.
The code above is highly unsafe with respect to threading, because it reads from and writes to a mutable variable on multiple threads.
The intermittent nature of the crash comes from the fact that the thread scheduler is non-deterministic. The crash won't happen when several threads accessing shared memory happen to have their execution scheduled in sequence rather than in parallel. It is therefore possible to "get lucky" some or even most of the time, but the code is still wrong.
The debugger printout is a good example of the danger. The thread that's paused is most likely reading from result while another thread performs an insertion.
NSMutableDictionary insertions are likely not atomic; example steps might be,
allocate memory for the new entry
copy the entry's key into the memory
copy the entry's value into the memory
If you read the dictionary from another thread between steps 1 and 2, you will see an entry for which memory has been allocated, but the memory contains no values.
The simplest fix is to switch to bk_each. bk_each does the same thing as bk_apply but it's implemented in a way that guarantees sequential execution.
So I have these two methods:
-(void)importEvents:(NSArray*)allEvents {
NSMutableDictionary *subjectAssociation = [[NSMutableDictionary alloc] init];
for (id thisEvent in allEvents) {
if (classHour.SubjectShort && classHour.Subject) {
[subjectAssociation setObject: classHour.Subject forKey:classHour.SubjectShort];
}
}
[self storeSubjects:subjectAssociation];
}
-(void)storeSubjects:(NSMutableDictionary*)subjects {
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *subjectsList = [documentsDir stringByAppendingPathComponent:#"Subjects.plist"];
[subjects writeToFile:subjectsList atomically:YES];
}
The first loops through an array of let's say 100 items, and builds a NSMutableDictionary of about 10 unique key/value pairs.
The second method writes this dictionary to a file for reference elsewhere in my app.
The first method is called quite often, and so is the second. However, I know, that once the dictionary is built and saved, its contents won't ever change, no matter how often I call these methods, since the number of possible values is just limited.
Question: given the fact that the second method essentially needs to be executed only once, should I add some lines that check if the file already exists, essentially adding code that needs to be executed, or can I just leave it as is, overwriting an existing file over and over again?
Should I care? I should add that I don't seem to suffer from any performance issues, so this is more of a philosophical/hygienic question.
thanks
It depends.
You say
once the dictionary is built and saved, its contents won't ever change
until they do :-)
If your app is not suffering from any performance issues on this particular loop I wouldn't try to cache for the reason that unless you somehow remember that you have a once-only write on the file you are storing up a bug for later.
This could be mitigated by using an intention revealing name on the method. i.e
-(void)storeSubjectsOnceOnlyPerLaunch:(NSDictionary*)subjects
If I got my time back for tracing down bugs caused by caching, I would have several days back in my life.
Your solution is totally over engineered, and has tons of potential to go wrong. What if the users drive is full? Does this file get backed up? Does it need backing up / are you wasting the users time backing it up? Can this fail? Are you handling it? You are concentrating on the entering and storing of data, you should be focusing on accessing that data.
I'd have a readwrite property allEvents and a property eventAssociations, declared readonly in the interface, but readwrite in the implementation file.
The allEvents setter stores allEvents and sets _eventAssociations to nil.
The eventAssociations getter checks whether _eventAssociations is nil and recalculates it when needed. A simple and bullet-proof pattern.
My code is crashing with an EXC_BAD_ACCESS error that I don't know how to debug.
This is the code:
NSUInteger lineCount = self.lineBeginnings.count;
NSUInteger lineBeginnings[lineCount];
[self.lineBeginnings getIndexes:lineBeginnings maxCount:lineCount inIndexRange:nil];
It crashes on the last line, with EXC_BAD_ACCESS (code=2, address=0x...).
Notice two lines above, it's able to read self.lineBeginnings perfectly, but in the debugger I get:
(lldb) p [self lineBeginnings]
error: Trying to put the stack in unreadable memory at: 0x7fff5d15e310.
(lldb) p _lineBeginnings
(NSMutableIndexSet *) $1 = 0x0000610000059b90
(lldb) po _lineBeginnings
[no Objective-C description available]
Also lineBeginnings doesn't show up properly in the GUI scope browser (all other variables do) and trying to "View Memory of lineBeginnings" gives a completely empty memory view.
The lineBeginnings variable is stored as a strong #property, it's a mutable index set created in the app delegate's init method and never removed while the app is running. There is a background operation queue that writes to it, but it switches to the main thread for all modifications using dispatch_sync(dispatch_get_main_queue()).
I'm not sure how to debug this further? It's hard to reproduce, I have to resize the window for up to a minute (which causes the lineBeginnings variable to be re-created on a background queue, a process that takes ~5 minutes when given 180MB of data), in order to make this crash occur.
It looks like a buffer overrun or something to me? How do I track it down?
The source code for the file is here: https://gist.github.com/abhibeckert/7128740 (the crash is on line 254).
In 180 MB there are probably millions of line beginnings? So you are allocating an array of millions of eight byte words on the stack. A thread's stack usually is not that large.
You should allocate the array on the heap, using malloc.
Your problem is called a stack overflow. Sounds familiar?
I had the same problem, after spending 2 days i found that my getter is calling several time because i was using self in getter.
if(! _openSectionIndexes) _openSectionIndexes = [NSMutableArray new];
if( _openSectionIndexes.count != _requests.count)
{
for (int i =0; i < _requests.count; i++)
{
[self.openSectionIndexes addObject: #(NO)];// here was the problem, replaced it with _openSectionIndexes
}
}
return _openSectionIndexes;
I want a thorough list regarding comparison between the two. Things I have known:
executeFetchRequest:
Message sent to MOC
Return an array of managed objects
Goal: fetch objects from persistent store to MOC
With table view: has nothing to do with table view
Frequency: often used in a loop, so could be called many many times
performFetch:
Message sent to FRC
After calling it, use fetchedObjects to return an array of managed objects
With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.
Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time
Please correct me if I am wrong and append the list. Thank you.
About executeFetchRequest:
Message sent to MOC
Yes
Return an array of managed objects
Yes, but you can also change the type of results you want to retrieve. In NSFetchRequest you can set a different result type with:
- (void)setResultType:(NSFetchRequestResultType)type
where NSFetchRequestResultType can be of different types. Taken from Apple doc:
enum {
NSManagedObjectResultType = 0x00,
NSManagedObjectIDResultType = 0x01,
NSDictionaryResultType = 0x02
NSCountResultType = 0x04
};
typedef NSUInteger NSFetchRequestResultType;
Goal: fetch objects from persistent store to MOC
Yes, creating a NSFetchRequest and performing a request, it the same as creating a SELECT statement in SQL. If you also use a NSPredicate it's the same as using SELECT-WHERE statement.
With table view: has nothing to do with table view
Yes, but with retrieved data you can populate a table
Frequency: often used in a loop, so could be called many many times
It depends, on what you want to achieve. It could be within a loop or not. Executing the request within a loop could have impact on performance but I would not be worried on that. Under the hood Core Data maintains a sort of cache mechanism. Every time you perform a request, if data are not in the cache, Core Data executes a round trip on your store (e.g. sql file) and populate the cache with the objects it has retrieved. If you perform the same query, the round trip will not performed again due to the cache mechanism. Anyway, you could avoid to execute a request within the run loop, simply moving that request outside the loop.
About performFetch:
Message sent to FRC
Yes
After calling it, use fetchedObjects to return an array of managed
objects
Yes, but you can also retrieve an object with [_fetchedResultsController objectAtIndexPath:indexPath]; if you are populating a specific cell within a table.
Here I really suggest to read a nice tutorial on NSFetchedResultsController
With table view: FRC is specifically for keeping managed objects and
table view rows in sync, and use performFetch to initialize that
process.
Yes, a NSFetchedResultsController works in combination with a NSManagedObjectContext for you. Furthermore, it enables lazy loading of data. Suppose you have 1000 elements you retrieve and you want to display them in a UITableView. Setting a request for a NSFetchRequest like:
[fetchRequest setFetchBatchSize:20];
and using it with an instance of a NSFetchedResultsController, it allows to load 20 elements at first. Then when you scroll, other 20 elements are loaded, and so on. Without a NSFetchedResultsController you must implement this behavior manually. Refer to the tutorial I provided for further info.
Frequency: often only once. Unless fetch request of FRC changes, no
need to call performFetch a second time
It depends on what you want to achieve. Most of the time you could call it once.
Hope that helps.
Edit
You have to call performFetch explicitly. I like to create a property for NSFetchedResultsController in my header file (.h) like
#property (nonatomic, strong, readonly) NSFetchedResultsController* fetchedResultsController;
and synthesize it in your implementation file (.m) like
#synthesize fetchedResultsController = _fetchedResultsController;
Then always within the .m file override the getter to create an new instance of it:
- (NSFetchedResultsController*)fetchedResultsController
{
// it already exists, so return it
if(_fetchedResultsController) return _fetchedResultsController;
// else create it and return
_fetchedResultsController = // alloc-init here with complete setup
return _fetchedResultsController;
}
Once done, within your class (for example in viewDidLoad method) use it like
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
You are comparing the wrong elements. NSFetchedResultsController uses the NSManagedObjectContext to perform the fetch, and under proper configuration, monitors the changes in the managed object context to verify the status of the fetch properties it is monitoring, but the actual fetches are done by the context. On both cases, NSManagedObjectContext does the fetch. The difference being that, using the NSManagedObjectContext directly, you get an NSArray type of object (the actual runtime class is different than an array you get using [NSArray array]), while NSFetchedResultsController has a different purpose (have a collection of results and monitor changes to the records and entity on its fetch request). In other words, NSFetchedResultsController works using the context, but it works different than just a simple collection of objects.
One observation: you shouldn't be using executeFetchRequest inside a loop, especially calling it "many many times". Each fetch has its performance cost. You can call executeFetchRequest once, and do a loop to check the result.
In my application there is searchBar. when we input a text, it will do functionGrab (grab data from internet and save it to coredata), example :
if we input "Hallo"
if([[dict objectForKey:#"Category"] isNotEmpty] && [[[dict objectForKey:#"Category"] objectAtIndex:0] class]!=[NSNull class]){
NSMutableArray * DownloadedTags =[dict objectForKey:#"Category"];
NSMutableSet * TagsReturn=[NSMutableSet set];
for(int i=0;i<[DownloadedTags count];i++){
NSString * Value=[DownloadedTags objectAtIndex:i];
Tag * thisTag= (Tag*)[GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"];
[TagsReturn addObject:thisTag];
}
NSMutableSet * manyManagedObjects = [BusinessToSave mutableSetValueForKey:#"Tags"];
[self removeDifferenceBetween2MutableManagedObjectSets:manyManagedObjects withDownloadedVersion:TagsReturn];
}
So each biz has many categories. WHat happen in multi threaded application is one thread put category. The other thread also put the same category before committing.
So, [GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"]; gives a new object even though some other thread already created the same object without knowing it.
If I synchronized the whole thing that the code would run serially and that'll be slow.
functionGrab:"H"
functionGrab:"Ha"
functionGrab:"Hal"
functionGrab:"Hall"
functionGrab:"Hallo"
something like,it do that functionGrab 5 times
I want to make functionGrab at background, but the problem is when I do that function without synchronized it will save more than one of data, so the result is there are 5 hallo words in my coredata, but if I do that with synchronized, it spent so much time, so slow..
is there any way to help my problem?
I do not recommended having more than one thread "creating" the same types of data for the exact reason you are running into.
I would suggest you queue all of your "creates" into a single thread and a single NSManagedObjectContext to avoid merge or duplication issues.
The other option would be to make the app Lion only and use the parent/child NSManagedObjectContext design and then your children will be more "aware" of each other.