The last two are identical; "atomic" is the default behavior (note that it is not actually a keyword; it is specified only by the absence of nonatomic
-- atomic
was added as a keyword in recent versions of llvm/clang).
Assuming that you are @synthesizing the method implementations, atomic vs. non-atomic changes the generated code. If you are writing your own setter/getters, atomic/nonatomic/retain/assign/copy are merely advisory. (Note: @synthesize is now the default behavior in recent versions of LLVM. There is also no need to declare instance variables; they will be synthesized automatically, too, and will have an _
prepended to their name to prevent accidental direct access).
With "atomic", the synthesized setter/getter will ensure that a whole value is always returned from the getter or set by the setter, regardless of setter activity on any other thread. That is, if thread A is in the middle of the getter while thread B calls the setter, an actual viable value -- an autoreleased object, most likely -- will be returned to the caller in A.
In nonatomic
, no such guarantees are made. Thus, nonatomic
is considerably faster than "atomic".
What "atomic" does not do is make any guarantees about thread safety. If thread A is calling the getter simultaneously with thread B and C calling the setter with different values, thread A may get any one of the three values returned -- the one prior to any setters being called or either of the values passed into the setters in B and C. Likewise, the object may end up with the value from B or C, no way to tell.
Ensuring data integrity -- one of the primary challenges of multi-threaded programming -- is achieved by other means.
Adding to this:
atomicity
of a single property also cannot guarantee thread safety when multiple dependent properties are in play.
Consider:
@property(atomic, copy) NSString *firstName;
@property(atomic, copy) NSString *lastName;
@property(readonly, atomic, copy) NSString *fullName;
In this case, thread A could be renaming the object by calling setFirstName:
and then calling setLastName:
. In the meantime, thread B may call fullName
in between thread A's two calls and will receive the new first name coupled with the old last name.
To address this, you need a transactional model. I.e. some other kind of synchronization and/or exclusion that allows one to exclude access to fullName
while the dependent properties are being updated.
Try this
#import <QuartzCore/QuartzCore.h> // not necessary for 10 years now :)
...
view.layer.cornerRadius = 5;
view.layer.masksToBounds = true;
Note: If you are trying to apply rounded corners to a UIViewController
's view, it should not be applied in the view controller's constructor, but rather in -viewDidLoad
, after view
is actually instantiated.
Best Answer
I managed to achieve the above effect, so thought I'd post an answer for the same.
If anyone has any suggestions about making this more effective, please feel free to contribute. I'll be sure to mark your answer as the correct one. :)
For doing this, you'll need to add a "custom attribute" to
NSAttributedString
.Basically, what that means is that you can add any key-value pair, as long as it is something that you can add to an
NSDictionary
instance. If the system does not recognize that attribute, it does nothing. It is up to you, as the developer, to provide a custom implementation and behavior for that attribute.For the purposes of this answer, let us assume I've added a custom attribute called:
@"MyRoundedBackgroundColor"
with a value of[UIColor greenColor]
.For the steps that follow, you'll need to have a basic understanding of how
CoreText
gets stuff done. Check out Apple's Core Text Programming Guide for understanding what's a frame/line/glyph run/glyph, etc.So, here are the steps:
NSAttributedString
.CTFramesetter
using thatNSAttributedString
instance.drawRect:
methodCTFrame
instance from theCTFramesetter
.CGPathRef
to create theCTFrame
. Make thatCGPath
to be the same as the frame in which you wish to draw the text.CTFrameGetLines(...)
, get all the lines in theCTFrame
you just created.CTFrameGetLineOrigins(...)
, get all the line origins for theCTFrame
.for loop
- for each line in the array ofCTLine
...CTLine
usingCGContextSetTextPosition(...)
.CTLineGetGlyphRuns(...)
get all the Glyph Runs (CTRunRef
) from theCTLine
.for loop
- for each glyphRun in the array ofCTRun
...CTRunGetStringRange(...)
.CTRunGetTypographicBounds(...)
.CTLineGetOffsetForStringIndex(...)
.runBounds
) using the values returned from the aforementioned functions.CTRunGetTypographicBounds(...)
requires pointers to variables to store the "ascent" and "descent" of the text. You need to add those to get the run height.CTRunGetAttributes(...)
.runBounds
).runBounds
, we know what area we want to paint - now we can use any of theCoreGraphis
/UIBezierPath
methods to draw and fill a rect with specific rounded corners.UIBezierPath
has a convenience class method calledbezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
that let's you round specific corners. You specify the corners using bit masks in the 2nd parameter.CTRunDraw(...)
.Regarding detecting that the attribute range extends over multiple runs, you can get the entire effective range of your custom attribute when the 1st run encounters the attribute. If you find that the length of the maximum effective range of your attribute is greater than the length of your run, you need to paint sharp corners on the right side (for a left to right script). More math will let you detect the highlight corner style for the next line as well. :)
Attached is a screenshot of the effect. The box on the top is a standard
UITextView
, for which I've set the attributedText. The box on the bottom is the one that has been implemented using the above steps. The same attributed string has been set for both the textViews.Again, if there is a better approach than the one that I've used, please do let me know! :D
Hope this helps the community. :)
Cheers!