Attributed Strings in Distributed Objects

  • I have an object, which responds to this message:

    - (NSAttributedString *)dummyWithStyle: (BOOL)withStyle ;
    {
    NSString *s = withStyle ? @"with Style" : @"without Style";
    NSMutableAttributedString *a = [ [ [ NSMutableAttributedString
    alloc ] initWithString: s ] autorelease ];
    if ( withStyle )
    {
      NSParagraphStyle *p = [ NSParagraphStyle defaultParagraphStyle ];
      unsigned int length = [ a length];
      NSRange all = NSMakeRange( 0, length );
      [ a addAttribute: NSParagraphStyleAttributeName  value: p  range:
    all ];
    };

    NSLog(@"%s will return %@", __FUNCTION__, a );
    return a;
    }

    If the flag "withStyle" is NO, or if sender and reveiver live in the
    same thread (i.e. [myObject isProxy ] = 0) then all is fine.

    But when I do: [proxyForMyObject dummyWithStyle: YES ] (with
    [proxyForMyObject isProxy] = 1) then the program will hang after
    writing the log message immediately before the return.

    (gdb) bt
    #0  0x9000b348 in mach_msg_trap ()
    #1  0x9000b29c in mach_msg ()
    #2  0x907ddba8 in __CFRunLoopRun ()
    #3  0x907dd4ac in CFRunLoopRunSpecific ()
    #4  0x92bd45a4 in -[NSConnection sendInvocation:] ()
    #5  0x92c89ee4 in -[NSDistantObject hash] ()
    #6  0x937e789c in attributeDictionaryHash ()
    #7  0x907dbcd8 in __CFSetFindBuckets1b ()
    #8  0x907db3f8 in CFSetGetValue ()
    #9  0x937e754c in +[NSAttributeDictionary newWithDictionary:] ()
    #10 0x92bfb630 in -[NSConcreteMutableAttributedString
    setAttributes:range:] ()
    #11 0x92c40ac4 in _NSReadMutableAttributedStringWithCoder ()
    #12 0x92c4086c in -[NSAttributedString initWithCoder:] ()
    #13 0x92bd8b58 in -[NSConcretePortCoder decodeRetainedObject] ()
    #14 0x92bd5330 in _NSWalkData ()
    #15 0x92bdce9c in -[NSInvocation decodeReturnValueWithCoder:] ()
    #16 0x92bd4854 in -[NSConnection sendInvocation:] ()
    #17 0x92bd3040 in -[NSObject(NSForwardInvocation) forward::] ()
    #18 0x90a450b0 in _objc_msgForward ()
    #19 0x00039c08 in -[SWDocument doButton:]  <-- the button which
    starts it all

    What am I doing wrong?

    Kind regards,

    Gerriet.
  • On 5 Sep 2007, at 08:23, Gerriet M. Denkmann wrote:

    > I have an object, which responds to this message:
    >
    > - (NSAttributedString *)dummyWithStyle: (BOOL)withStyle ;
    > {
    > NSString *s = withStyle ? @"with Style" : @"without Style";
    > NSMutableAttributedString *a = [ [ [ NSMutableAttributedString
    > alloc ] initWithString: s ] autorelease ];
    > if ( withStyle )
    > {
    > NSParagraphStyle *p    = [ NSParagraphStyle defaultParagraphStyle ];
    > unsigned int length = [ a length];
    > NSRange all = NSMakeRange( 0, length );
    > [ a addAttribute: NSParagraphStyleAttributeName  value: p  range:
    > all ];
    > };
    >
    > NSLog(@"%s will return %@", __FUNCTION__, a );
    > return a;
    > }
    >
    > If the flag "withStyle" is NO, or if sender and reveiver live in
    > the same thread (i.e. [myObject isProxy ] = 0) then all is fine.
    >
    > But when I do: [proxyForMyObject dummyWithStyle: YES ] (with
    > [proxyForMyObject isProxy] = 1) then the program will hang after
    > writing the log message immediately before the return.

    I think (though I could be wrong) that some object is being proxied
    in the main thread (possibly the attributed string itself, though I
    would have thought that these were always copied... it could be
    something to do with the attribute structure, or maybe your
    NSParagraphStyle that's causing the problem).

    Anyway, as a result, your thread needs to run a run loop in order to
    process messages from the main thread.  I'm guessing that your thread
    has terminated or is doing something other than running a run loop.

    One possible way around this would be to subclass e.g.
    NSParagraphStyle (or whatever is causing the problem) and override -
    replacementObjectForPortCoder: to return self (assuming that it
    doesn't already).  As I say, I think NSAttributedStrings are copied
    rather than proxied, and your backtrace seems to support that, so I
    don't think it's the string itself that's the issue (and if it was,
    you could just use "bycopy" in a declaration somewhere).

    If you get your code to crash again in the same way, then do

      frame 4

    and look at the invocation (depending on where you are in -
    sendInvocation:, and which processor architecture you're on, it could
    be on the stack or in a register... with luck, GDB will tell you what
    the argument was when you change frame, but it doesn't always get it
    right so you might need to step through code), you should be able to
    work out which object it is that's causing you grief.

    Or, you could run a run loop in your other thread.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On 5 Sep 2007, at 13:05, Alastair Houghton wrote:

    >
    > On 5 Sep 2007, at 08:23, Gerriet M. Denkmann wrote:
    >
    >> I have an object, which responds to this message:
    >>
    >> - (NSAttributedString *)dummyWithStyle: (BOOL)withStyle ;
    >> {
    >> NSString *s = withStyle ? @"with Style" : @"without Style";
    >> NSMutableAttributedString *a = [ [ [ NSMutableAttributedString
    >> alloc ] initWithString: s ] autorelease ];
    >> if ( withStyle )
    >> {
    >> NSParagraphStyle *p    = [ NSParagraphStyle defaultParagraphStyle ];
    >> unsigned int length = [ a length];
    >> NSRange all = NSMakeRange( 0, length );
    >> [ a addAttribute: NSParagraphStyleAttributeName  value: p
    >> range: all ];
    >> };
    >>
    >> NSLog(@"%s will return %@", __FUNCTION__, a );
    >> return a;
    >> }
    >>
    >> If the flag "withStyle" is NO, or if sender and reveiver live in
    >> the same thread (i.e. [myObject isProxy ] = 0) then all is fine.
    >>
    >> But when I do: [proxyForMyObject dummyWithStyle: YES ] (with
    >> [proxyForMyObject isProxy] = 1) then the program will hang after
    >> writing the log message immediately before the return.
    >
    > I think (though I could be wrong) that some object is being proxied
    > in the main thread (possibly the attributed string itself, though I
    > would have thought that these were always copied... it could be
    > something to do with the attribute structure, or maybe your
    > NSParagraphStyle that's causing the problem).

    The main thread has a method doButton: which contains:

    NSLog(@"%s will do: [ %s dummyWithStyle: %s ] in main thread %p",
    __FUNCTION__, [o isProxy] ? "proxy" : "real", style ? "YES" : "NO",
    [NSThread currentThread]);
    NSAttributedString *a = [o dummyWithStyle: style] ;
    NSLog(@"%s got %s AttributedString = \"%@\"", __FUNCTION__, [a
    isProxy] ? "proxy" : "real", [a string]);

    This is the output:

    ... -[SWDocument doButton:] will do: [ proxy dummyWithStyle: NO ] in
    main thread 0x306cf0
    ... -[SXLeaf dummyWithStyle:] will return "without Style" in thread
    0x3a0300
    ... -[SWDocument doButton:] got real AttributedString = "without Style"

    no problem so far. But now with NSParagraphStyle added:

    ... -[SWDocument doButton:] will do: [ proxy dummyWithStyle: YES ] in
    main thread 0x306cf0
    ... -[SXLeaf dummyWithStyle:] will return "with Style" in thread
    0x3a0300

    Now the app is not crashing at all, it just hangs.
    When I run it under the debugger and hit the "Pause" button, then I
    will see the stack as quoted.
    >
    > Anyway, as a result, your thread needs to run a run loop in order
    > to process messages from the main thread.  I'm guessing that your
    > thread has terminated or is doing something other than running a
    > run loop.

    Well, the main thread obviously has a run loop; and the other thread
    also has one. I followed the example in "Communicating With
    Distributed Objects" in "Multithreading Programming Topics".

    >
    > One possible way around this would be to subclass e.g.
    > NSParagraphStyle (or whatever is causing the problem) and override -
    > replacementObjectForPortCoder: to return self (assuming that it
    > doesn't already).  As I say, I think NSAttributedStrings are copied
    > rather than proxied, and your backtrace seems to support that, so I
    > don't think it's the string itself that's the issue (and if it was,
    > you could just use "bycopy" in a declaration somewhere).

    Well, maybe it's NSParagraphStyle, maybe it's any kind of text
    attachment, maybe any deep structure (what about an NSArray
    containing objects, which again contain arrays, which again... - how
    deep a copy is NSConnection prepared to make?) - I am just too tired
    to find out.

    I just got some other idea and made myself a new method:
    - (NSData *)archivedDummyWithStyle: (BOOL)withStyle ;
    {
    // ... same as before in dummyWithStyle:
    // followed by:
    NSData *c = [NSArchiver archivedDataWithRootObject: a];
    return c;
    }

    And now I can get my attributed strings with or without paragraph
    styles. Might take a few extra milliseconds, but I don't care.
    >
    > Or, you could run a run loop in your other thread.
    As mentioned, the worker thread does have a run loop.

    Kind regards,

    Gerriet.
  • On 5 Sep 2007, at 15:34, Gerriet M. Denkmann wrote:

    >
    > On 5 Sep 2007, at 13:05, Alastair Houghton wrote:
    >
    >>
    >> On 5 Sep 2007, at 08:23, Gerriet M. Denkmann wrote:
    >>
    >>> I have an object, which responds to this message:
    >>>
    >>> - (NSAttributedString *)dummyWithStyle: (BOOL)withStyle ;
    >>> {
    >>> NSString *s = withStyle ? @"with Style" : @"without Style";
    >>> NSMutableAttributedString *a = [ [ [ NSMutableAttributedString
    >>> alloc ] initWithString: s ] autorelease ];
    >>> if ( withStyle )
    >>> {
    >>> NSParagraphStyle *p    = [ NSParagraphStyle defaultParagraphStyle ];
    >>> unsigned int length = [ a length];
    >>> NSRange all = NSMakeRange( 0, length );
    >>> [ a addAttribute: NSParagraphStyleAttributeName  value: p
    >>> range: all ];
    >>> };
    >>>
    >>> NSLog(@"%s will return %@", __FUNCTION__, a );
    >>> return a;
    >>> }
    >>>
    >>> If the flag "withStyle" is NO, or if sender and reveiver live in
    >>> the same thread (i.e. [myObject isProxy ] = 0) then all is fine.
    >>>
    >>> But when I do: [proxyForMyObject dummyWithStyle: YES ] (with
    >>> [proxyForMyObject isProxy] = 1) then the program will hang after
    >>> writing the log message immediately before the return.
    >>
    >> I think (though I could be wrong) that some object is being
    >> proxied in the main thread (possibly the attributed string itself,
    >> though I would have thought that these were always copied... it
    >> could be something to do with the attribute structure, or maybe
    >> your NSParagraphStyle that's causing the problem).
    >
    > The main thread has a method doButton: which contains:
    >
    > NSLog(@"%s will do: [ %s dummyWithStyle: %s ] in main thread %p",
    > __FUNCTION__, [o isProxy] ? "proxy" : "real", style ? "YES" : "NO",
    > [NSThread currentThread]);
    > NSAttributedString *a = [o dummyWithStyle: style] ;
    > NSLog(@"%s got %s AttributedString = \"%@\"", __FUNCTION__, [a
    > isProxy] ? "proxy" : "real", [a string]);
    >
    > This is the output:
    >
    > ... -[SWDocument doButton:] will do: [ proxy dummyWithStyle: NO ]
    > in main thread 0x306cf0
    > ... -[SXLeaf dummyWithStyle:] will return "without Style" in thread
    > 0x3a0300
    > ... -[SWDocument doButton:] got real AttributedString = "without
    > Style"
    >
    > no problem so far. But now with NSParagraphStyle added:
    >
    > ... -[SWDocument doButton:] will do: [ proxy dummyWithStyle: YES ]
    > in main thread 0x306cf0
    > ... -[SXLeaf dummyWithStyle:] will return "with Style" in thread
    > 0x3a0300
    >
    > Now the app is not crashing at all, it just hangs.
    > When I run it under the debugger and hit the "Pause" button, then I
    > will see the stack as quoted.
    >>
    >> Anyway, as a result, your thread needs to run a run loop in order
    >> to process messages from the main thread.  I'm guessing that your
    >> thread has terminated or is doing something other than running a
    >> run loop.
    >
    > Well, the main thread obviously has a run loop; and the other
    > thread also has one. I followed the example in "Communicating With
    > Distributed Objects" in "Multithreading Programming Topics".
    >
    >>
    >> One possible way around this would be to subclass e.g.
    >> NSParagraphStyle (or whatever is causing the problem) and override
    >> -replacementObjectForPortCoder: to return self (assuming that it
    >> doesn't already).  As I say, I think NSAttributedStrings are
    >> copied rather than proxied, and your backtrace seems to support
    >> that, so I don't think it's the string itself that's the issue
    >> (and if it was, you could just use "bycopy" in a declaration
    >> somewhere).
    >
    > Well, maybe it's NSParagraphStyle, maybe it's any kind of text
    > attachment, maybe any deep structure (what about an NSArray
    > containing objects, which again contain arrays, which again... -
    > how deep a copy is NSConnection prepared to make?) - I am just too
    > tired to find out.

    Well - I did try after all:
    It is not NSParagraphStyle - it seems to be any kind of attachment (I
    tried an NSTextAttachment).
    And NSArrays are returned as real arrays, containing the same stuff
    (not proxies to the things in the original array) - it is a shallow
    copy.

    So NSAttributed strings seem to be just broken with DO.

    Kind regards,

    Gerriet.
  • On 5 Sep 2007, at 14:34, Gerriet M. Denkmann wrote:

    > ... -[SWDocument doButton:] will do: [ proxy dummyWithStyle: YES ]
    > in main thread 0x306cf0
    > ... -[SXLeaf dummyWithStyle:] will return "with Style" in thread
    > 0x3a0300
    >
    > Now the app is not crashing at all, it just hangs.
    > When I run it under the debugger and hit the "Pause" button, then I
    > will see the stack as quoted.

    What is the other thread actually doing when this happens?  (i.e. you
    need to get the backtrace from the other thread as well, not just the
    backtrace from your main thread... all that's telling you is that
    it's trying to send a message over DO and isn't getting a reply).

    > I just got some other idea and made myself a new method:
    > - (NSData *)archivedDummyWithStyle: (BOOL)withStyle ;
    > {
    > // ... same as before in dummyWithStyle:
    > // followed by:
    > NSData *c = [NSArchiver archivedDataWithRootObject: a];
    > return c;
    > }
    >
    > And now I can get my attributed strings with or without paragraph
    > styles. Might take a few extra milliseconds, but I don't care.

    :-)  It probably doesn't take significantly longer, actually, since
    DO would have been doing something very similar before (just using
    NSPortCoder rather than NSArchiver), and archiving an NSData object
    is likely to be very fast.

    Anyway, it's good that you've found a workaround.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today