2

Environment: Mac OS X 10.9, Xcode 5.0.2, ARC disabled.

Question: How free memory of property after all thread finish job. See example below.

I`am create mini example with one button “(IBAction)btnRun:(id)sender”. Example read txt file and fill NSArray property (sharedListWords). Then run two thread and each thread display words,
see OUT Section. When threads finished a job, property (self.sharedListWords) not released! But I want free memory which allocated for (self.sharedListWords) property. The action “btnRun” exit before threads finished job and I cant release (self.sharedListWords) in this action.

How free memory of property (self.sharedListWords) after threads finish job? Yes good answer here by create dependency operation jobFinished(), but how correct release property?

And this is my first multithreading program on Objective-c, I will be glad for adjustments.


AppDelegate.h:

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>
{
    volatile int32_t sharedIndex;   // Shared between threads, current index in array
    NSOperationQueue* operationQueue;
}
@property (assign) IBOutlet NSWindow *window;
// Shared between threads, list of words
@property (atomic, retain) NSArray* sharedListWords;
- (void)worker;

@end

AppDelegate.m:

#import "AppDelegate.h"

@implementation AppDelegate

- (IBAction)btnRun:(id)sender
{
    // Read txt file dictionary of words, where is each words in new line.
    NSString* dictionaryFilePath = [NSString stringWithFormat:@"/Users/admin/dictionary.txt"];
    NSString* fileContents = [NSString stringWithContentsOfFile:dictionaryFilePath
                                                       encoding:NSUTF8StringEncoding error:nil];
    // Get array of string separated by new line
    self.sharedListWords = [fileContents componentsSeparatedByCharactersInSet:
                                [NSCharacterSet newlineCharacterSet]];

    //self.sharedListWords = @[@"one",@"two",@"three",@"four",@"five",@"six",@"seven",@"eight",@"nine",@"ten"];

    self->sharedIndex = -1;

    int numberOfThreads = 2;

    // Run method working() in separate threads
    for(int i=0; i<numberOfThreads; ++i)
    {
        //////////////////////////////////////////
        // Create a thread
        // Create new NSOperatin object with function puting in @selector() for run in other thread.
        NSOperation* startBruteOper = [[NSInvocationOperation alloc]
                                       initWithTarget:self selector:@selector(worker) object:nil];
        // Add the operation to the queue and let it to be executed.
        [operationQueue addOperation:startBruteOper];
        [startBruteOper release];
        /////////////////////////////////////////
    }
}

- (void)worker
{
    unsigned long countWords = [self.sharedListWords count];

    int32_t index = 0;

    // Use atomic operation for thread safe
    while( (index = OSAtomicIncrement32( &(self->sharedIndex) ) ) < countWords )
    {
        NSLog(@"[%@] working on \"%@\"",
              [NSThread currentThread],
              [self.sharedListWords objectAtIndex:index]);
    }

    NSLog(@"[%@] work is finish.", [NSThread currentThread]);
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Multithreading queue list
    operationQueue = [[NSOperationQueue alloc] init];
}

@end

OUT Section:

[<NSThread: num = 2}] working on "one"
[<NSThread: num = 3}] working on "two"
[<NSThread: num = 2}] working on "three"
[<NSThread: num = 3}] working on "four"
[<NSThread: num = 2}] working on "five"
[<NSThread: num = 3}] working on "six"
[<NSThread: num = 2}] working on "seven"
[<NSThread: num = 3}] working on "eight"
[<NSThread: num = 2}] working on "nine"
[<NSThread: num = 3}] working on "ten"
[<NSThread: num = 2}] work is finish.
[<NSThread: num = 3}] work is finish.
Community
  • 1
  • 1
Jarikus
  • 774
  • 8
  • 18
  • 1
    possible duplicate of [iOS - How to know when NSOperationQueue finish processing a few operations?](http://stackoverflow.com/questions/9998532/ios-how-to-know-when-nsoperationqueue-finish-processing-a-few-operations) – Martin R May 01 '14 at 17:12
  • Martin R> and I can run [self.sharedListWords release]; in handler NSOperationQueue finish? – Jarikus May 01 '14 at 17:32
  • few minuts I`am testing ) – Jarikus May 01 '14 at 17:38
  • Martin R> I`am make dependency handler jobFinished() see updated code, and first time it work fine, but when I press second time button it return segmantation fault on property self.sharedListWords. Maybe property need differently released? – Jarikus May 01 '14 at 17:51
  • 1
    I am so used to ARC that I almost cannot remember the MRC times anymore :-) But I think you should assign `self.sharedListWords = nil` in `jobFinished`, instead of calling `release`. – Martin R May 01 '14 at 17:53
  • Yes self.sharedListWords = nil; work perfect. I`am have identical memory use before press button and after finished gob. Thank you very much Martin. – Jarikus May 01 '14 at 18:03
  • I have added my comments as an answer. - I think you should not edit your code in the question to the working code, because that makes the comments/answers unintellegible for future readers. The *question* should state the problem and an *answer* the solution. – Martin R May 01 '14 at 18:16
  • Martin R> I`am issued corrected code below. – Jarikus May 01 '14 at 18:26

2 Answers2

2

This is correct code, after Martins instructions.

AppDelegate.h:

    #import <Cocoa/Cocoa.h>

    @interface AppDelegate : NSObject <NSApplicationDelegate>
    {
        volatile int32_t sharedIndex;   // Shared between threads, current index in array
        NSOperationQueue* operationQueue;
    }
    @property (assign) IBOutlet NSWindow *window;
    // Shared between threads, list of words
    @property (atomic, retain) NSArray* sharedListWords;
    - (void)worker;

    @end

AppDelegate.m:

    #import "AppDelegate.h"

    @implementation AppDelegate

    - (IBAction)btnRun:(id)sender
    {
        // Read txt file dictionary of words, where is each words in new line.
        NSString* dictionaryFilePath = [NSString stringWithFormat:@"/Users/admin/dictionary.txt"];
        NSString* fileContents = [NSString stringWithContentsOfFile:dictionaryFilePath
                                                           encoding:NSUTF8StringEncoding error:nil];
        // Get array of string separated by new line
        self.sharedListWords = [fileContents componentsSeparatedByCharactersInSet:
                                    [NSCharacterSet newlineCharacterSet]];

        //self.sharedListWords = @[@"one",@"two",@"three",@"four",@"five",@"six",@"seven",@"eight",@"nine",@"ten"];

        self->sharedIndex = -1;

        int numberOfThreads = 2;

        NSOperation* jobFinishedOper = [[NSInvocationOperation alloc]
                                    initWithTarget:self selector:@selector(jobFinished) object:nil];

        // Run method working() in separate threads
        for(int i=0; i<numberOfThreads; ++i)
        {
            //////////////////////////////////////////
            // Create a thread
            // Create new NSOperatin object with function puting in @selector() for run in other thread.
            NSOperation* startBruteOper = [[NSInvocationOperation alloc]
                                           initWithTarget:self selector:@selector(worker) object:nil];
            // Add the operation to the queue and let it to be executed.
            [operationQueue addOperation:startBruteOper];
            [jobFinishedOper addDependency:startBruteOper]; // 'jobFinishedOper' run only when 'startBruteOper' finished!
            [startBruteOper release];
            /////////////////////////////////////////
        }
        // 'jobFinishedOper' run only when all prevous operation is finished!
        [operationQueue addOperation:jobFinishedOper];
        [jobFinishedOper release];
    }

    - (void)worker
    {
        unsigned long countWords = [self.sharedListWords count];

        int32_t index = 0;

        // Use atomic operation for thread safe
        while( (index = OSAtomicIncrement32( &(self->sharedIndex) ) ) < countWords )
        {
            NSLog(@"[%@] working on \"%@\"",
                  [NSThread currentThread],
                  [self.sharedListWords objectAtIndex:index]);
        }

        NSLog(@"[%@] work is finish.", [NSThread currentThread]);
    }

    - (void)jobFinished
    {
        self.sharedListWords = nil;
    }

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        // Multithreading queue list
        operationQueue = [[NSOperationQueue alloc] init];
    }

    @end
Jarikus
  • 774
  • 8
  • 18
1

You can add another operation which is dependent on all "worker" operations, as described in

That operation is run after all its dependencies have finished, so you can call

self.sharedListWords = nil;

in the finished operation to release the array.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382