Date   

NSData base64Encoding vs. base64EncodedStringWithOptions

Alex Zavatone
 

Xcode 9.2, iOS 11.2, Objective-C


Looking at the header for NSData, and base64EncodedStringWithOptions: NSDataBase64EncodingOptions,

the options are these starting on line 50 of NSData.h:

typedef NS_OPTIONS(NSUInteger, NSDataBase64EncodingOptions) {
    // Use zero or one of the following to control the maximum line length after which a line ending is inserted. No line endings are inserted by default.
    NSDataBase64Encoding64CharacterLineLength = 1UL << 0,
    NSDataBase64Encoding76CharacterLineLength = 1UL << 1,

    // Use zero or more of the following to specify which kind of line ending is inserted. The default line ending is CR LF.
    NSDataBase64EncodingEndLineWithCarriageReturn = 1UL << 4,
    NSDataBase64EncodingEndLineWithLineFeed = 1UL << 5,

}

Questions

- What did the deprecated base64Encoding do with the line endings?  How long were lines and what happened at a line ending?

The typedef for NSDataBase64EncodingOptions, 0 is given the text, NSDataBase64Encoding64CharacterLineLength, indicating that a line is 64 characters.  

The documentation for this states that “NSDataBase64Encoding64CharacterLineLength Set the maximum line length to 64 characters, after which a line ending is inserted.”.

The comment on line 52 for NSDataBase64Encoding64CharacterLineLength states something different.     // Use zero or one of the following to control the maximum line length after which a line ending is inserted. No line endings are inserted by default. 

It states that a line ending is inserted and no line endings are inserted by default.

- Which is the default value, 0?  nil?   I’d expect that it is 0, but the documentation for 0 states that a line ending is inserted and that no line endings are inserted by default.  If 0 is the default, how is this possible?

- What happens for NSDataBase64Encoding76CharacterLineLength?

Can someone please clarify what Apple’s confusing documentation doesn’t?

Thank you,

Alex Zavatone


Re: Swift and KVC

Alex Zavatone
 


On Apr 3, 2018, at 12:54 AM, Quincey Morris <quinceymorris@...> wrote:

On Apr 2, 2018, at 22:39 , Marco S Hyman <marc@...> wrote:

 I didn’t expect I’d have to quit and relaunch again because I changed one line.   Should have known better.

Xcode 9.x has an intermittent bug where it gets confused about what you’ve changed. (It looks like the actual displayed text is inconsistent with some internal memory representation.) It doesn’t happen often, but when it does, the best solution is to close all open source files, and quitting is the quickest way to do that.


Apparently Xcode has become Windows 95.  When in doubt, reboot.

: /


Re: Swift and KVC

Quincey Morris
 

On Apr 2, 2018, at 22:39 , Marco S Hyman <marc@...> wrote:

 I didn’t expect I’d have to quit and relaunch again because I changed one line.   Should have known better.

Xcode 9.x has an intermittent bug where it gets confused about what you’ve changed. (It looks like the actual displayed text is inconsistent with some internal memory representation.) It doesn’t happen often, but when it does, the best solution is to close all open source files, and quitting is the quickest way to do that.


Re: Swift and KVC

Marco S Hyman
 

Does it persist if you quit and relaunch Xcode?
Perhaps I’m expecting too much from Xcode. I’d quit and relaunched in order to get output for the code I’d posted. I didn’t expect I’d have to quit and relaunch again because I changed one line. Should have known better.

Yeah, it works.


Re: Swift and KVC

Quincey Morris
 

On Apr 2, 2018, at 21:09 , Marco S Hyman <marc@...> wrote:

One curiosity, though... that works in my application but I still get no output in the Playground sample code.

Hmm. I did try it in a playground before I posted, and the output is there. (Xcode 9.3)

Does it persist if you quit and relaunch Xcode?


Re: Swift and KVC

Marco S Hyman
 

You need:

extension Foo {
@objc var name: String {
return "foo name"
}
}
(since ‘value(forKey:)’ needs an Obj-C property name).
Many, many thanks. One curiosity, though... that works in my application but I still get no output in the Playground sample code.

Marc


Re: Swift and KVC

Quincey Morris
 

On Apr 2, 2018, at 19:44 , Marco S Hyman <marc@...> wrote:

Is it me (likely) or is it swift

It’s you, but you can plausibly blame it on Swift.

You need:

extension Foo {
   @objc var name: String {
       return "foo name"
   }
}

(since ‘value(forKey:)’ needs an Obj-C property name).


Swift and KVC

Marco S Hyman
 

This simple little playground doesn’t print any value for the last line. It is a minimization of an issue I’m having in a larger program where the key-value can not be found. Is it me (likely) or is it swift and KVC?

Xcode 9.3

```
//: Playground - noun: a place where people can play

import Cocoa

var str = "Hello, playground"

class Foo: NSObject {
// ...
}

extension Foo {
var name: String {
return "foo name"
}
}

let foo = Foo()
print(foo.name) // prints “foo name"
let fooName = foo.value(forKey: "name")
print(fooName as! String) // prints nothing
```


Marc


Re: Internationalisation of MainMenu

Gerriet M. Denkmann
 

On 1 Apr 2018, at 10:29, Quincey Morris <quinceymorris@...> wrote:

Another problem is missing context in MainMenu.strings:
But the only context given is:
/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */
It was reported in the developer forums recently that the localization comment field that you can enter in IB (the very last field of the Identity inspector) isn’t being output for Mac targets, though it is for iOS. I don’t know if this is fixed in Xcode 9.3. If it does work, this is where you can specify the context.
Seems not to work in Xcode Version 9.3 (9E145).

Also: this is really something Xcode should to for me.

Kind regards,

Gerriet.


Re: Internationalisation of MainMenu

Quincey Morris
 

On Mar 31, 2018, at 18:31 , Gerriet M. Denkmann <g@...> wrote:

What is right: SPACE or NO-BREAK SPACE ? Does this make any difference?

I would suggest you use a regular space, since a menu item can’t word-wrap onto a new line.

It *might* make a difference if the font specifies a different width for the non-breaking space than a regular space. Since this is not a typeset environment, you can’t rely on a layout manager to reassign values to space characters, so you want all the spaces to be the same width. Hence use the same character. (In practice, there’s likely no noticeable difference.)

Another problem is missing context in MainMenu.strings:

But the only context given is:
/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */

It was reported in the developer forums recently that the localization comment field that you can enter in IB (the very last field of the Identity inspector) isn’t being output for Mac targets, though it is for iOS. I don’t know if this is fixed in Xcode 9.3. If it does work, this is where you can specify the context.


Re: Internationalisation of MainMenu

Gerriet M. Denkmann
 

On 30 Mar 2018, at 05:06, Gary L. Wade <garywade@...> wrote:

https://developer.apple.com/download/more/?name=Glossaries
Thanks a lot for this link!

Looking at the German stuff, I see that for example “Print…” gets translated:
29 times: "Drucken …" NO-BREAK SPACE; HORIZONTAL ELLIPSIS
56 times: “Drucken …" HORIZONTAL ELLIPSIS

What is right: SPACE or NO-BREAK SPACE ? Does this make any difference?


Another problem is missing context in MainMenu.strings:

“Cut” would have certainly a quite different translation in:

Edit
Cut
Paste

Hairdressing
Cut
Perm

But the only context given is:
/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */

But I guess nothing can be done to get this needed context into MainMenu.strings.


Kind regards

Gerriet.


--
Gary L. Wade
http://www.garywade.com/

On Mar 29, 2018, at 2:43 PM, Quincey Morris <quinceymorris@...> wrote:

On Mar 28, 2018, at 22:46 , Gerriet M. Denkmann <g@...> wrote:

Is there a way to get this official translation?
It seems like there ought to be, but I don’t think there is.

I would assume that menu items that get added to your app at runtime (e.g. the ones at the bottom of the Edit menu) will be localized for you. It’s also documented somewhere that the words “Undo” and “Redo” are automatically localized when you supply undo action names.

Other than that, you’re probably on your own.


Re: Internationalisation of MainMenu

Gary L. Wade
 

On Mar 29, 2018, at 2:43 PM, Quincey Morris <quinceymorris@...> wrote:

On Mar 28, 2018, at 22:46 , Gerriet M. Denkmann <g@...> wrote:

Is there a way to get this official translation? 

It seems like there ought to be, but I don’t think there is.

I would assume that menu items that get added to your app at runtime (e.g. the ones at the bottom of the Edit menu) will be localized for you. It’s also documented somewhere that the words “Undo” and “Redo” are automatically localized when you supply undo action names.

Other than that, you’re probably on your own.


FYI, Xcode 9.3 was just released.

Alex Zavatone
 


Re: Internationalisation of MainMenu

Quincey Morris
 

On Mar 28, 2018, at 22:46 , Gerriet M. Denkmann <g@...> wrote:

Is there a way to get this official translation? 

It seems like there ought to be, but I don’t think there is.

I would assume that menu items that get added to your app at runtime (e.g. the ones at the bottom of the Edit menu) will be localized for you. It’s also documented somewhere that the words “Undo” and “Redo” are automatically localized when you supply undo action names.

Other than that, you’re probably on your own.


Internationalisation of MainMenu

Gerriet M. Denkmann
 

I am trying to make an macOS app international.

On thing is MainMenu.strings. This contains lots of stuff which must have an official translation (e.g. into German) already.
(e.g. Edit → Cut).

Is there a way to get this official translation?

I want all standard stuff translated using the official nomenclature, so that I have only to translate menu items which are specific to my app.

Gerriet.


Re: NSCondition

Dave
 

Hi Again,

On 12 Mar 2018, at 18:43, Sandor Szatmari <admin.szatmari.net@...> wrote:

Dave,

On Mon, Mar 12, 2018 at 1:28 PM, Dave <dave@...> wrote:
Hi Again Sandor,

Yes, I’ve had similar fun doing the same thing here!

Thanks for the heads up on isCancelled and I’ll take your advice and store it as a property.

I’m not sure I understand what you mean with the call to “wait” - please see code revised code below.

See below for what I'm talking about with the -wait call...

Additionally, think about where your testing -isCancelled and breaking out of the loop(s); you have multiple loops. Does that break, break you out of all loops or just the innermost loop. Do you want to break out of all loops... if your terminating the thread, probably.

Where you've placed it, It looks like you can remove an object from the queue and the break the loop without processing it... if the application were not terminating and you were going to resume processing, you would loose that one item...

You also might want to check it at the head of your loop... and/or check it before you call -wait inside my suggested loop... In general where and how often you check -isCanceled can determine how responsive and well behaved an app/thread is to terminating... Just be sure to make sure your leaving everything in an expected/sane state; tearing things down properly... releasing objects... unlocking your conditions... etc.
Yes, that is what is does in effect, the outer while loop is "while ([[NSThread currentThread] isCancelled] == NO)” which does the same thing.

It only processes the Object if there is one at the head of the queue, the act of getting the head item also removes it. For every “wait" it receives it may process more than one object before waiting again (e.g. it tries to drain the queue), this guards against over or under signalling. Also, it does use a “Boolean Predicate”, this this if the queue is empty or not, e.g. [array count] != 0, if there are no objects waiting to be processed then its False otherwise its True., I’ve been stress testing it and it stands up to everything I can throw at it, the worst case is we get choppy results under extreme circumstances. Basically think of a VERY precise volume control, these are a LOT of steps from no sound to full volume, but after that differences are small.

All the Best
Dave


Re: NSCondition

Sandor Szatmari
 

Dave,

On Mon, Mar 12, 2018 at 1:28 PM, Dave <dave@...> wrote:
Hi Again Sandor,

Yes, I’ve had similar fun doing the same thing here!

Thanks for the heads up on isCancelled and I’ll take your advice and store it as a property.

I’m not sure I understand what you mean with the call to “wait” - please see code revised code below.

See below for what I'm talking about with the -wait call...

Additionally, think about where your testing -isCancelled and breaking out of the loop(s); you have multiple loops.  Does that break, break you out of all loops or just the innermost loop.  Do you want to break out of all loops... if your terminating the thread, probably.  

Where you've placed it, It looks like you can remove an object from the queue and the break the loop without processing it... if the application were not terminating and you were going to resume processing, you would loose that one item...

You also might want to check it at the head of your loop...  and/or check it before you call -wait inside my suggested loop...  In general where and how often you check -isCanceled can determine how responsive and well behaved an app/thread is to terminating...  Just be sure to make sure your leaving everything in an expected/sane state; tearing things down properly... releasing objects... unlocking your conditions... etc.

 

I think I’m already doing what you suggest?

All the Best
Dave

-(void) consumerTask
{
while ([[NSThread currentThread] isCancelled] == NO)
        {
        @autoreleasepool
                {
//**
//**    Wait for a Signal
//**
                [self.pQueueTaskConsumerTaskWakeUpCondition lock];

                   // --------------------------------------------------------------------------------------------
                   //  New Loop
                   // 
                   //  If the NSCondition ever gets signaled, wait again if the queue is empty.
                   //  The documentation for NSCondition explains that the condition when signaled will try to acquire the lock and the proceed
                   //  so once we acquire the lock here the next thing we will do is check the queue.
                   //  If is it not empty, we end the loop and proceed to process.
                   //  If it is empty we call wait again... (This is testing the predicate that has been referred to.)
                   //     The documentation states that calling wait will unlock the condition and and then wait to be signaled...
                   //
                   while ( [self.pQueueTaskProcessingQueue count] == 0 )
                    [self.pQueueTaskConsumerTaskWakeUpCondition wait];
 
//**
//**    Get Head Object - Locked
//**
                self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
                self.pQueueTaskProcessingQueueCount = [self.pQueueTaskProcessingQueue queueGetCount];
                [self.pQueueTaskConsumerTaskWakeUpCondition unlock];

//**
//**    Loop to Process All Items
//**
                while (self.pQueueTaskLastObjectRemoved != nil)
                        {
                        if ([[NSThread currentThread] isCancelled] == YES)
                                break;

//**
//**    Process Object - Unlocked
//**
                        [self processObject:self.pQueueTaskLastObjectRemoved];

//**
//**    Get Head Object - Locked
//**
                        [self.pQueueTaskConsumerTaskWakeUpCondition lock];
                        self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
                        self.pQueueTaskProcessingQueueCount = [self.pQueueTaskProcessingQueue queueGetCount];
                        [self.pQueueTaskConsumerTaskWakeUpCondition unlock];
                        }
                }
        }
}

> On 12 Mar 2018, at 14:34, Sandor Szatmari <admin.szatmari.net@...> wrote:
>
> Dave,
>
> That's great I'm glad it's working!  I had fun working up the sample... :)  I let it run for a couple of hours watching the processes add a consume data.  It was interesting to see how the random generation and consumption of data fought with each other.
>
> The way I saw it, you conflated the processing of the head object's contents with the processing of the array.  Only the array access (array processing) needs to be protected by the NSCondition...
>
> Before you call it a day consider the following.  I believe you really should have the nested while loop in there around the call to -wait.  My interpretation as to why this is necessary...  I believe it prevents you from acquiring the lock when it's unnecessary to hold the lock; guarding against deadlock conditions and any unnecessary dead time in any thread that may be waiting on the  lock.
>
> The documentation for NSCondition specifically talks about spurious wakes and wanting to guard against this.  Check it out: https://developer.apple.com/documentation/foundation/nscondition.  It has a nice bit of pseudo code illustrating how it should be used.  Reading it, I felt the documentation for this class is very good and worth a read or a reread...
>
> Cheers,
> Sandor
>
>
> On Mon, Mar 12, 2018 at 7:06 AM, Dave <dave@...> wrote:
> Hi,
>
> Thanks for the sample code Sandor, I’ve now got it working correctly now and I’m really pleased with it. I realised what was wrong just before I went to sleep last night and just added the fix and it works! The major difference in the way your project works is that you have a separate Producer Thread, in my case, a delegate method of the producer task is called with the Data on the Main Thread and you must completely deal with the data it passes before the method returns...
>
> The problem was that it was processing the whole queue with the Condition Locked, so nothing else could get it, this is the revised Consumer Task and it works really well:
>
> -(void) consumerTask
> {
> while (YES)
>         {
>         [self.pQueueTaskConsumerTaskWakeUpCondition lock];
>         [self.pQueueTaskConsumerTaskWakeUpCondition wait];
>
> //**
> //**    Get Head Object - Locked
> //**
>         self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
>         [self.pQueueTaskConsumerTaskWakeUpCondition unlock];
>
> //**
> //**    Process the Entire Queue
> //**
>         while (self.pQueueTaskLastObjectRemoved != nil)
>                 {
> //**
> //**    Process Object - Unlocked
> //**
>                 [self processObject:self.pQueueTaskLastObjectRemoved];
>
> //**
> //**    Get Head Object - Locked
> //**
>                 [self.pQueueTaskConsumerTaskWakeUpCondition lock];
>                 self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
>                 [self.pQueueTaskConsumerTaskWakeUpCondition unlock];
>                 }
>         }
> }
>
> I noticed that your Consumer thread checks “isCancelled”, I’ve added this to my version, although I’m not sure it is needed.
>
> I’ve written this as two generalised classes, a Task Class and a Queue Class, to use it all you need to do is created the Task Object with a Delegate that contains two methods one that is called just before an Object is added to the Queue and one that is called to process the Object. Once I’ve had a chance to tidy it up I’ll send it to anyone that is interested.
>
> Thanks again,
> All the Best
> Dave
>
>
>
>
>
>






Re: NSCondition

Dave
 

Hi Again Sandor,

Yes, I’ve had similar fun doing the same thing here!

Thanks for the heads up on isCancelled and I’ll take your advice and store it as a property.

I’m not sure I understand what you mean with the call to “wait” - please see code revised code below.

I think I’m already doing what you suggest?

All the Best
Dave

-(void) consumerTask
{
while ([[NSThread currentThread] isCancelled] == NO)
{
@autoreleasepool
{
//**
//** Wait for a Signal
//**
[self.pQueueTaskConsumerTaskWakeUpCondition lock];
[self.pQueueTaskConsumerTaskWakeUpCondition wait];

//**
//** Get Head Object - Locked
//**
self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
self.pQueueTaskProcessingQueueCount = [self.pQueueTaskProcessingQueue queueGetCount];
[self.pQueueTaskConsumerTaskWakeUpCondition unlock];

//**
//** Loop to Process All Items
//**
while (self.pQueueTaskLastObjectRemoved != nil)
{
if ([[NSThread currentThread] isCancelled] == YES)
break;

//**
//** Process Object - Unlocked
//**
[self processObject:self.pQueueTaskLastObjectRemoved];

//**
//** Get Head Object - Locked
//**
[self.pQueueTaskConsumerTaskWakeUpCondition lock];
self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
self.pQueueTaskProcessingQueueCount = [self.pQueueTaskProcessingQueue queueGetCount];
[self.pQueueTaskConsumerTaskWakeUpCondition unlock];
}
}
}
}

On 12 Mar 2018, at 14:34, Sandor Szatmari <admin.szatmari.net@...> wrote:

Dave,

That's great I'm glad it's working! I had fun working up the sample... :) I let it run for a couple of hours watching the processes add a consume data. It was interesting to see how the random generation and consumption of data fought with each other.

The way I saw it, you conflated the processing of the head object's contents with the processing of the array. Only the array access (array processing) needs to be protected by the NSCondition...

Before you call it a day consider the following. I believe you really should have the nested while loop in there around the call to -wait. My interpretation as to why this is necessary... I believe it prevents you from acquiring the lock when it's unnecessary to hold the lock; guarding against deadlock conditions and any unnecessary dead time in any thread that may be waiting on the lock.

The documentation for NSCondition specifically talks about spurious wakes and wanting to guard against this. Check it out: https://developer.apple.com/documentation/foundation/nscondition. It has a nice bit of pseudo code illustrating how it should be used. Reading it, I felt the documentation for this class is very good and worth a read or a reread...

Cheers,
Sandor


On Mon, Mar 12, 2018 at 7:06 AM, Dave <dave@...> wrote:
Hi,

Thanks for the sample code Sandor, I’ve now got it working correctly now and I’m really pleased with it. I realised what was wrong just before I went to sleep last night and just added the fix and it works! The major difference in the way your project works is that you have a separate Producer Thread, in my case, a delegate method of the producer task is called with the Data on the Main Thread and you must completely deal with the data it passes before the method returns...

The problem was that it was processing the whole queue with the Condition Locked, so nothing else could get it, this is the revised Consumer Task and it works really well:

-(void) consumerTask
{
while (YES)
{
[self.pQueueTaskConsumerTaskWakeUpCondition lock];
[self.pQueueTaskConsumerTaskWakeUpCondition wait];

//**
//** Get Head Object - Locked
//**
self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
[self.pQueueTaskConsumerTaskWakeUpCondition unlock];

//**
//** Process the Entire Queue
//**
while (self.pQueueTaskLastObjectRemoved != nil)
{
//**
//** Process Object - Unlocked
//**
[self processObject:self.pQueueTaskLastObjectRemoved];

//**
//** Get Head Object - Locked
//**
[self.pQueueTaskConsumerTaskWakeUpCondition lock];
self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
[self.pQueueTaskConsumerTaskWakeUpCondition unlock];
}
}
}

I noticed that your Consumer thread checks “isCancelled”, I’ve added this to my version, although I’m not sure it is needed.

I’ve written this as two generalised classes, a Task Class and a Queue Class, to use it all you need to do is created the Task Object with a Delegate that contains two methods one that is called just before an Object is added to the Queue and one that is called to process the Object. Once I’ve had a chance to tidy it up I’ll send it to anyone that is interested.

Thanks again,
All the Best
Dave






Re: NSCondition

Sandor Szatmari
 

Regarding the call to -isCancelled in the consumer thread... presumably you will have a handle to this thread somewhere and want to -cancel it gracefully prior to termination...  Or cancel it and restart it...  

For whatever your reason may be... checking the -isCanceled state gives you the change to break out of the threads loop...

So instead of -detachThread.... you could use -initWithSelector.... and hold onto this handle to your thread...  then you can either make it directly available as a property or define an interface on you class to allow outside interaction...

Sandor

On Mon, Mar 12, 2018 at 7:06 AM, Dave <dave@...> wrote:
Hi,

Thanks for the sample code Sandor, I’ve now got it working correctly now and I’m really pleased with it. I realised what was wrong just before I went to sleep last night and just added the fix and it works! The major difference in the way your project works is that you have a separate Producer Thread, in my case, a delegate method of the producer task is called with the Data on the Main Thread and you must completely deal with the data it passes before the method returns...

The problem was that it was processing the whole queue with the Condition Locked, so nothing else could get it, this is the revised Consumer Task and it works really well:

-(void) consumerTask
{
while (YES)
        {
        [self.pQueueTaskConsumerTaskWakeUpCondition lock];
        [self.pQueueTaskConsumerTaskWakeUpCondition wait];

//**
//**    Get Head Object - Locked
//**
        self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
        [self.pQueueTaskConsumerTaskWakeUpCondition unlock];

//**
//**    Process the Entire Queue
//**
        while (self.pQueueTaskLastObjectRemoved != nil)
                {
//**
//**    Process Object - Unlocked
//**
                [self processObject:self.pQueueTaskLastObjectRemoved];

//**
//**    Get Head Object - Locked
//**
                [self.pQueueTaskConsumerTaskWakeUpCondition lock];
                self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
                [self.pQueueTaskConsumerTaskWakeUpCondition unlock];
                }
        }
}

I noticed that your Consumer thread checks “isCancelled”, I’ve added this to my version, although I’m not sure it is needed.

I’ve written this as two generalised classes, a Task Class and a Queue Class, to use it all you need to do is created the Task Object with a Delegate that contains two methods one that is called just before an Object is added to the Queue and one that is called to process the Object. Once I’ve had a chance to tidy it up I’ll send it to anyone that is interested.

Thanks again,
All the Best
Dave






Re: NSCondition

Sandor Szatmari
 

Dave,

That's great I'm glad it's working!  I had fun working up the sample... :)  I let it run for a couple of hours watching the processes add a consume data.  It was interesting to see how the random generation and consumption of data fought with each other.  

The way I saw it, you conflated the processing of the head object's contents with the processing of the array.  Only the array access (array processing) needs to be protected by the NSCondition...

Before you call it a day consider the following.  I believe you really should have the nested while loop in there around the call to -wait.  My interpretation as to why this is necessary...  I believe it prevents you from acquiring the lock when it's unnecessary to hold the lock; guarding against deadlock conditions and any unnecessary dead time in any thread that may be waiting on the  lock.

The documentation for NSCondition specifically talks about spurious wakes and wanting to guard against this.  Check it out: https://developer.apple.com/documentation/foundation/nscondition.  It has a nice bit of pseudo code illustrating how it should be used.  Reading it, I felt the documentation for this class is very good and worth a read or a reread...

Cheers,
Sandor


On Mon, Mar 12, 2018 at 7:06 AM, Dave <dave@...> wrote:
Hi,

Thanks for the sample code Sandor, I’ve now got it working correctly now and I’m really pleased with it. I realised what was wrong just before I went to sleep last night and just added the fix and it works! The major difference in the way your project works is that you have a separate Producer Thread, in my case, a delegate method of the producer task is called with the Data on the Main Thread and you must completely deal with the data it passes before the method returns...

The problem was that it was processing the whole queue with the Condition Locked, so nothing else could get it, this is the revised Consumer Task and it works really well:

-(void) consumerTask
{
while (YES)
        {
        [self.pQueueTaskConsumerTaskWakeUpCondition lock];
        [self.pQueueTaskConsumerTaskWakeUpCondition wait];

//**
//**    Get Head Object - Locked
//**
        self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
        [self.pQueueTaskConsumerTaskWakeUpCondition unlock];

//**
//**    Process the Entire Queue
//**
        while (self.pQueueTaskLastObjectRemoved != nil)
                {
//**
//**    Process Object - Unlocked
//**
                [self processObject:self.pQueueTaskLastObjectRemoved];

//**
//**    Get Head Object - Locked
//**
                [self.pQueueTaskConsumerTaskWakeUpCondition lock];
                self.pQueueTaskLastObjectRemoved = [self.pQueueTaskProcessingQueue queueGetHead];
                [self.pQueueTaskConsumerTaskWakeUpCondition unlock];
                }
        }
}

I noticed that your Consumer thread checks “isCancelled”, I’ve added this to my version, although I’m not sure it is needed.

I’ve written this as two generalised classes, a Task Class and a Queue Class, to use it all you need to do is created the Task Object with a Delegate that contains two methods one that is called just before an Object is added to the Queue and one that is called to process the Object. Once I’ve had a chance to tidy it up I’ll send it to anyone that is interested.

Thanks again,
All the Best
Dave





901 - 920 of 1460