Date   

Re: string pointer

Dave
 

On 1 Mar 2018, at 19:18, Steve Mills <sjmills@...> wrote:


On Mar 1, 2018, at 13:05:37, Quincey Morris <quinceymorris@...> wrote:

— This solution:

NSUInteger c = [self computeFor:42 reason:needReason ? &s : nil];
is AFAIK a bit dangerous. The “computeFor:reason:” method stores an *autoreleased* string pointer into its output parameter, so “s” will end up containing this autoreleased pointer when “needReason” is true. That autoreleased pointer is a ticking time bomb that will explode if it’s still being used somewhere when its autorelease pool is drained (which could be a *lot* later, leading to hard-to-find bug) without being owned somewhere else. Whether this is a problem depends on what you do with “s” next.
I disagree that it's dangerous. It's the standard way code is written when a parameter is passed back by reference. How many times in Apple code do you see something like:
Just because you see it in “Apple Code” doesn’t mean its the best or safest way of doing something. Just look at the quality of code coming out of Apple these days, its awful, in fact just look at the mess that is called XCode!

Autoreleasing bugs are really difficult to find which is why I avoid using AU like the plague!

All the Best
Dave


Re: Changing head item on a (GCD) Queue

Quincey Morris
 

On Mar 5, 2018, at 05:04 , Dave <dave@...> wrote:

I’m trying to figure out the best (modern) way to this which I assume is to use GCD?

Not necessarily. Both NSOperationQueue and GCD can cancel un-started operations, but NSOperationQueue has a mechanism for cancelling running operations too. Overall, you probably get more control over the queue with NSOperationQueue.


Changing head item on a (GCD) Queue

Dave
 

Hi All,

I need to do some changes to a Mac Objective-C project to do the following:

1. Data is Sent to a Delegate method in a class.

2. The data is validated by this method and if ok, it needs to be processed in background, e.g. added to a Queue.

3. There is a possibility that the data coming in makes items already queued for processing invalid, this happens when a burst of data is sent in a small time-frame. In this case, I want to examine the head of the queue (e.g. the next data block to be processed) and if this new data makes it invalid, overwrite it.

As an example I have this on the queue:

Head: Data.type = NewLocation, Data.Location = 123,

The new Data is

Data.type = NewLocation, Data.Location = 124,

Instead of adding this to the queue, I want to overwrite the exist Head item with the new DataLocation (124).

I’m trying to figure out the best (modern) way to this which I assume is to use GCD? Any suggestions, pointers or sample code on how to do this would be greatly appreciated.

All the Best
Dave


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 5 Mar 2018, at 11:33 am, Gary L. Wade <garywade@...> wrote:

You do want your subviews drawn, right?

Of course, but they’re not drawn by -drawRect:, they’re drawn by an internal private method that calls -drawRect: Normally, drawRect is concerned only with the drawing pertaining to itself and nothing else, not even its subviews. If you set a breakpoint in -drawRect:, you can see the call stack:

#0 0x000000010000987c in -[GCDigitView drawRect:] at /Users/grahamcox/Projects/GCDigitArrayControl/GCDigitArrayControl/GCDigitArrayControl.m:1190
#1 0x00007fff409d8c21 in _NSViewDrawRect ()
#2 0x00007fff409e5eb9 in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:shouldChangeFontReferenceColor:] ()
#3 0x00007fff409e5889 in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inContext:shouldChangeFontReferenceColor:] ()
#4 0x00007fff4027746c in __46-[NSView(NSLayerKitGlue) drawLayer:inContext:]_block_invoke ()
#5 0x00007fff40277000 in -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] ()
#6 0x00007fff402769d1 in -[NSView(NSLayerKitGlue) drawLayer:inContext:] ()
#7 0x00007fff4dc85ad5 in CABackingStoreUpdate_ ()
#8 0x00007fff4dc859b4 in ___ZN2CA5Layer8display_Ev_block_invoke ()
#9 0x00007fff4dc851b7 in -[CALayer _display] ()
#10 0x00007fff40275996 in _NSBackingLayerDisplay ()
#11 0x00007fff4026a80d in -[_NSViewBackingLayer display] ()
#12 0x00007fff4dc76d3b in CA::Layer::display_if_needed(CA::Transaction*) ()
#13 0x00007fff4dc767f9 in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()
#14 0x00007fff4dc75894 in CA::Context::commit_transaction(CA::Transaction*) ()
#15 0x00007fff4dc7543d in CA::Transaction::commit() ()
#16 0x00007fff40a21658 in __65+[CATransaction(NSCATransaction) NS_setFlushesWithDisplayRefresh]_block_invoke ()
#17 0x00007fff42b66127 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()


This is for my subview. As you can see, there are a lot of internal things going on, but the superView’s -drawRect: is not one of them.

In any case, calling [super drawRect:] in my outer view makes no difference to whether the subviews are drawn or not.

—Graham


Re: Getting IB_DESIGNABLE to work properly

Gary L. Wade
 

You do want your subviews drawn, right?

On Mar 4, 2018, at 4:12 PM, Graham Cox <graham@...> wrote:



On 5 Mar 2018, at 10:46 am, Gary L. Wade <garywade@...> wrote:

Looking at the code, it appears you’re missing a call to [super drawRect:dirtyRect] in your own drawRect: method.


I’ve never called super, nor understand why it would be needed. I know the boilerplate calls it, but it appears to do nothing.

If there has been a change in the recommendation for this since 10.2 or so, then I missed it.

—Graham


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 5 Mar 2018, at 11:12 am, Graham Cox <graham@...> wrote:



On 5 Mar 2018, at 9:57 am, Quincey Morris <quinceymorris@...> wrote:

On Mar 4, 2018, at 14:31 , Quincey Morris <quinceymorris@...> wrote:

you can compensate for that
Works, too!

- (void) prepareForInterfaceBuilder
{
mIsInterfaceBuilder = YES;
}
I’ve gone for something slightly different that’s a little more generic (it’ll work for any view, not relying on it being a specific class). It still wouldn’t work for a layer backed or hosting view though, but it’s good for this case. Thanks for the idea.


//… -drawRect: ….


if( mIsInterfaceBuilder )
{
for( NSView* subView in self.subviews )
{
if( !subView.isHidden )
{
NSRect svf = subView.frame;

if( NSIntersectsRect( svf, dirtyRect ))
{
NSAffineTransform* tfm = [NSAffineTransform transform];
NSRect dr = [self convertRect:dirtyRect toView:subView];

[tfm translateXBy:svf.origin.x yBy:svf.origin.y];
[NSGraphicsContext saveGraphicsState];
[tfm concat];
[subView drawRect:dr];
[NSGraphicsContext restoreGraphicsState];
}
}
}
}


I’m still inclined to report this as an enhancement/bug against IB though, I feel it *should* work without this workaround.

—Graham


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 5 Mar 2018, at 9:57 am, Quincey Morris <quinceymorris@...> wrote:

On Mar 4, 2018, at 14:31 , Quincey Morris <quinceymorris@...> wrote:

you can compensate for that
Works, too!

- (void) prepareForInterfaceBuilder
{
mIsInterfaceBuilder = YES;
}

- (void) drawRect:(NSRect) dirtyRect
{


if (mIsInterfaceBuilder)
{
for (GCDigitView* subview in self.subviews)
{
if (subview.isHidden || ![subview isKindOfClass: [GCDigitView class]])
continue;

[subview drawRect: dirtyRect offset: YES];
}
}
}
- (void) drawRect:(NSRect) dirtyRect
{
[self drawRect: dirtyRect offset: NO];
}

- (void) drawRect:(NSRect) dirtyRect offset: (BOOL) isOffset
{
NSRect dbr = [self highlightRectForMask:kGCBothHalvesHighlight];
if (isOffset)
dbr = NSOffsetRect (dbr, self.frame.origin.x, self.frame.origin.y);
Cool - I’ll give that a shot. Thanks!

—Graham


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 5 Mar 2018, at 10:46 am, Gary L. Wade <garywade@...> wrote:

Looking at the code, it appears you’re missing a call to [super drawRect:dirtyRect] in your own drawRect: method.
I’ve never called super, nor understand why it would be needed. I know the boilerplate calls it, but it appears to do nothing.

If there has been a change in the recommendation for this since 10.2 or so, then I missed it.

—Graham


Re: Getting IB_DESIGNABLE to work properly

Gary L. Wade
 

Looking at the code, it appears you’re missing a call to [super drawRect:dirtyRect] in your own drawRect: method.

On Mar 3, 2018, at 8:29 PM, Graham Cox <graham@...> wrote:

Hi all,

I’m working on a small custom view project (Mac), and have it working generally quite well.

I decided to make it IB_DESIGNABLE, and that too mostly works - I can see the view in IB rendered with the correct background and border colours, I see all the inspectable properties, and I have established that the internal subviews it creates are constructed. This isn’t easy to determine with IB, because it doesn’t display anything I log; I have to rely on NSBeep() to tell me if a given bit of code ran and so on - this is very unsatisfactory, because I can’t debug the view in the IB case, only in the normal runtime case, where it works perfectly.

The problem I have is that the subviews I create as part of my view exist, but never draw. That means the appearance in IB is blank.

I’m out of ideas on what I need to do to get this to work. 

If anyone’s got a few minutes to take a quick look at the project and see if they can spot the problem, I’d be very grateful: http://s3.amazonaws.com/Mapdiva/Source/Test%20Projects/GCDigitArrayControl.zip

This shows the difference of the views in IB and at runtime:

<Screen Shot 2018-03-04 at 3.23.08 pm.png><Screen Shot 2018-03-04 at 3.26.35 pm.png>
Each digit in the display is a separate subview rendering one character, created programmatically when the view is instantiated.

—Graham




Re: Getting IB_DESIGNABLE to work properly

Quincey Morris
 

On Mar 4, 2018, at 14:31 , Quincey Morris <quinceymorris@...> wrote:

you can compensate for that

Works, too!

- (void) prepareForInterfaceBuilder
{
mIsInterfaceBuilder = YES;
}

- (void) drawRect:(NSRect) dirtyRect 
{

if (mIsInterfaceBuilder)
{
for (GCDigitView* subview in self.subviews)
{
if (subview.isHidden || ![subview isKindOfClass: [GCDigitView class]])
continue;

[subview drawRect: dirtyRect offset: YES];
}
}
}

- (void) drawRect:(NSRect) dirtyRect
{
[self drawRect: dirtyRect offset: NO];
}

- (void) drawRect:(NSRect) dirtyRect offset: (BOOL) isOffset
{
NSRect dbr = [self highlightRectForMask:kGCBothHalvesHighlight];
if (isOffset)
dbr = NSOffsetRect (dbr, self.frame.origin.x, self.frame.origin.y);


Re: Getting IB_DESIGNABLE to work properly

Quincey Morris
 

On Mar 4, 2018, at 14:29 , Quincey Morris <quinceymorris@...> wrote:

Unfortunately, they draw in the wrong place

Oh, of course, by calling their drawRect, it makes them draw in parent bounds coordinates, not their own bounds coordinates, but you can compensate for that.


Re: Getting IB_DESIGNABLE to work properly

Quincey Morris
 

On Mar 4, 2018, at 14:10 , Graham Cox <graham@...> wrote:

the IB ‘agent’ for previewing an IB_DESIGNABLE view directly calls -drawRect: on the view - it does not use the proper hierarchical view rendering process

An interesting observation. I went back to your test project and made the digit-array drawRect call drawRect on the subview, and they draw in IB!

Unfortunately, they draw in the wrong place. If you can figure out why their frame is not what you originally set (or compensate somehow), you presumably could get this to draw right.

This assumes that your digit-array view knows it’s drawing for IB, and it *can* know that. There’s a “prepareForInterfaceBuilder” method that’s supposedly called to let you know you’re in that environment.


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 4 Mar 2018, at 10:43 pm, Jeremy Hughes via Groups.Io <moon.rabbit@...> wrote:

Hi Graham,

Are you setting needsDisplay for each of the subviews?

Jeremy

Yes - the view works properly in the normal runtime case.

I’ve determined that the reason is that the IB ‘agent’ for previewing an IB_DESIGNABLE view directly calls -drawRect: on the view - it does not use the proper hierarchical view rendering process. So any subviews are just not drawn. It may be doing this for some good reason - performance, or security perhaps (since it’s running ‘foreign’ code that it can’t vet) - but this minimises the usefulness of the IB_DESIGNABLE feature. I’m considering whether to report this as a bug/enhancement request, since it means no view that uses subviews it creates programmatically will render properly.

As Quincey suggested, in this case I could reorganise my view to be flat and get around this, but it would be a better solution for IB to render the view using -display instead of -drawRect: - it would make IB better. Since they keep taking away stuff from us (e.g. the support for IB plug-ins) it would be nice if they could occasionally give something back.


—Graham


Re: Getting IB_DESIGNABLE to work properly

Jeremy Hughes
 

Hi Graham,

Are you setting needsDisplay for each of the subviews?

Jeremy


Re: Getting IB_DESIGNABLE to work properly

Graham Cox
 

On 4 Mar 2018, at 4:14 pm, Quincey Morris <quinceymorris@...> wrote:

AFAICT it doesn’t work like that. The canvas isn’t showing the view hierarchy, it’s showing the objects in the design hierarchy that you can reveal on the left. Of course, that represents the view hierarchy that will be created by nib loading, but that’s not happening at design/editing time.
Thanks for having a look,

I see what you’re saying, but IB has to instantiate the view (at least once) to capture what it renders. It must also redo this capture whenever any of the properties change. I can prove that by adding NSBeep() to the -drawRect method - it beeps on every property change. By the same approach I know that my subviews are created - but a beep in their -drawRect: method doesn’t occur.

If I have this code in my view’s -drawRect:, it also beeps:

if( self.subviews.count > 8 )
NSBeep();

This demonstrates that the subviews are there, so it’s not that IB is forcing the subviews to be empty after instantiating the view, it’s just somehow preventing them rendering - I wondered maybe by invoking -drawRect: instead of -display. I can’t think why they’d do this, it makes the feature a lot less useful than it could be. However, even if I call -display on all the subviews myself as part of -drawRect:, they don’t draw, so there’s something tricky going on.



I think your best choice is to eliminate the subviews, and do all the work in the parent view, using an internal “selected digit” mechanism. Your subviews aren’t so terribly complex that this is hard, just tedious.
Not worth the rewrite for this extra nicety, TBH.

—Graham


Re: Getting IB_DESIGNABLE to work properly

Quincey Morris
 

On Mar 3, 2018, at 20:29 , Graham Cox <graham@...> wrote:

If anyone’s got a few minutes to take a quick look at the project and see if they can spot the problem

AFAICT it doesn’t work like that. The canvas isn’t showing the view hierarchy, it’s showing the objects in the design hierarchy that you can reveal on the left. Of course, that represents the view hierarchy that will be created by nib loading, but that’s not happening at design/editing time.

I think your best choice is to eliminate the subviews, and do all the work in the parent view, using an internal “selected digit” mechanism. Your subviews aren’t so terribly complex that this is hard, just tedious.


Getting IB_DESIGNABLE to work properly

Graham Cox
 

Hi all,

I’m working on a small custom view project (Mac), and have it working generally quite well.

I decided to make it IB_DESIGNABLE, and that too mostly works - I can see the view in IB rendered with the correct background and border colours, I see all the inspectable properties, and I have established that the internal subviews it creates are constructed. This isn’t easy to determine with IB, because it doesn’t display anything I log; I have to rely on NSBeep() to tell me if a given bit of code ran and so on - this is very unsatisfactory, because I can’t debug the view in the IB case, only in the normal runtime case, where it works perfectly.

The problem I have is that the subviews I create as part of my view exist, but never draw. That means the appearance in IB is blank.

I’m out of ideas on what I need to do to get this to work. 

If anyone’s got a few minutes to take a quick look at the project and see if they can spot the problem, I’d be very grateful: http://s3.amazonaws.com/Mapdiva/Source/Test%20Projects/GCDigitArrayControl.zip

This shows the difference of the views in IB and at runtime:

Each digit in the display is a separate subview rendering one character, created programmatically when the view is instantiated.

—Graham



Re: string pointer

Quincey Morris
 

On Mar 1, 2018, at 11:18 , Steve Mills <sjmills@...> wrote:

I disagree that it's dangerous. It's the standard way code is written when a parameter is passed back by reference. […]

(I tried to find Charles’s post about this, but didn’t succeed.)

Everything you said after the first sentence is absolutely true. That is the standard way, and your suggestion helped rewrite the code according to  the standard way without a lot of fuss.

But it’s still dangerous. You can Try It at Home™. Put this code in a new app’s delegate:

- (void) computeReason: (NSObject **) reason
{
*reason = [[NSObject alloc] init];
}

- (void) applicationDidFinishLaunching: (NSNotification*) notification
{
NSObject *s;
// @autoreleasepool {
[self computeReason: &s];
// }
NSLog (@"%@", s);
}

Run it and it works. Now comment out the 2 commented lines. Run it and it crashes.

That’s because “s” contains an autoreleased pointer. It’s fine so long as nothing drains the autorelease pool, but if something does, it fails. The fact that “s” is supposed to be __strong is (unfortunately) not respected by the called method, which thinks it’s __autoreleasing.

Normally, it’s not a problem, because the drain of the autorelease pool is up at the main event loop, long after these methods have returned. However, if the pointer ends up in a different pool (as in this contrived example) with a shorter lifetime, that’s bad.

This example is not so very contrived, though. It isn’t uncommon to use a local autorelease pool for compute-bound loops, and if any errors happen during the loop the app can crash. Also, if you send pointers off to GCD queues, the autorelease pool lifetimes are hard to predict.

BTW, the original code would *never* fail, for an unrelated reason. The pointer in that case was to a literal NSString, which isn’t reference counted, and is alive forever.



Re: string pointer

Steve Mills
 

On Mar 1, 2018, at 13:05:37, Quincey Morris <quinceymorris@...> wrote:

— This solution:

NSUInteger c = [self computeFor:42 reason:needReason ? &s : nil];
is AFAIK a bit dangerous. The “computeFor:reason:” method stores an *autoreleased* string pointer into its output parameter, so “s” will end up containing this autoreleased pointer when “needReason” is true. That autoreleased pointer is a ticking time bomb that will explode if it’s still being used somewhere when its autorelease pool is drained (which could be a *lot* later, leading to hard-to-find bug) without being owned somewhere else. Whether this is a problem depends on what you do with “s” next.
I disagree that it's dangerous. It's the standard way code is written when a parameter is passed back by reference. How many times in Apple code do you see something like:

NSError* err;

if([thing doStuffAndReturnError:&err])
;
else
; // Handle error.

They don't add any decoration to the local variable err. And every time you autocomplete a method body that includes a reference param, Xcode includes __autoreleasing between the splats:

-(BOOL) doStuffAndReturnError:(NSError*__autoreleasing*)err;

They even say this in the "Transitioning to ARC Release Notes":

• __autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.

Then goes on to explain how the compiler takes care of things by rewriting it for you.

--
Steve Mills
Drummer, Mac geek


Re: string pointer

Quincey Morris
 

On Mar 1, 2018, at 04:13 , Gerriet M. Denkmann <g@...> wrote:

But my real problem is this:

BOOL needReason = …
NSString *s;
NSString **stringPointer = needReason ? &s : NULL;
NSUInteger c = [ self computeFor: 42 reason: stringPointer ]; // sometimes want to know the reason

I cannot figure out how to declare the stringPointer without getting compiler warnings.

So, here’s what’s going on. It’s a bit messy.

— A pointer has an ownership attribute, which is __strong by default, but can be explicitly specified as __weak or __autoreleasing. In your code, variable “s” is __strong by default.

— A pointer to a pointer needs to know the ownership of the pointee, and there is no default (in most cases), so you get the error "Pointer to non-const type 'NSString *' with no explicit ownership” for your declaration of “stringPointer”.

— If you specify the actual ownership explicitly:

NSString *__strong*stringPointer = needReason ? &s : NULL;

that error goes away, but there is still an error on the call: “Passing address of non-local object to __autoreleasing parameter for write-back”.

That’s because a pointer to a pointer that’s a method parameter *does* have a default pointee ownership, “__autoreleasing”. (When you added the explicit “__autoreleasing”, you didn’t change anything that wasn’t already assumed.)

— So, you can declare your “computeFor:” method like this:

- (NSUInteger)computeFor: (NSUInteger)arg  reason: (NSString *__strong*)reason

and everything would be just great, and this is the safest route. The only irritation is that every "NSString**” you pass into “computeFor:reason:” is going to need to be explicitly annotated, including those that pass through other methods, so the need to annotate is going to ripple outwards across your code.

— This solution:

NSUInteger c = [self computeFor:42 reason:needReason ? &s : nil];

is AFAIK a bit dangerous. The “computeFor:reason:” method stores an *autoreleased* string pointer into its output parameter, so “s” will end up containing this autoreleased pointer when “needReason” is true. That autoreleased pointer is a ticking time bomb that will explode if it’s still being used somewhere when its autorelease pool is drained  (which could be a *lot* later, leading to hard-to-find bug) without being owned somewhere else. Whether this is a problem depends on what you do with “s” next.

FWIW, all APIs that have a "NSError**outError” parameter have this same danger. (Charles Srstka pointed this out a few months ago.) Our apps work because the unowned pointer usually gets thrown away fast, or gets stored somewhere that gives it an extra retain.

941 - 960 of 1460