Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
Part of the fun of teaching a live class is fielding questions and thinking on your feet. Once I was showing some code that used @properties extensively in the interface, and one of the students asked “What are all the little fiddly bits that go after the @property
? I’ve used retain and assign and nonatomic, but don’t really know what they really do.” (There’s a TL;DR at the end of all the @property
attributes if you just want a quick cheat-sheet.)
For many Objective-C programmers, @property
is a magical talisman. Add something like this to your code to give your puppy object a name:
@property (copy, nonatomic) NSString *puppyName;
Stick it into an Xcode code snippet, and paste it in and edit when new ones are needed.
At @property
is a property declaration which serves two purposes: it declares one or two methods, and succinctly describes the runtime semantics when you’re using those methods.
@property declarations are shorthand for declaring accessor methods. These are methods that let you retrieve a value from an object (Hey puppy, what’s your name?) or change a value (Welcome home puppy! Your name is now Rumpelstiltskin). Here’s a property declaration in a class declaration:
@interface Puppy : NSObject
@property NSString *puppyName;
@end
The compiler will act like you actually declared two method declarations instead:
@interface Puppy : NSObject
- (NSString *) puppyName;
- (void) setPuppyName: (NSString *) newPuppyName;
@end
The convention it uses is: treat the property name as the getter and tacking “set” on to the property name for the setter, unless told otherwise.
What if you didn’t want your setter or getter to have the default name? Some true/false properties might read better with a name like “isHousebroken” than just “housebroken”. You can add some API control to your property description:
@property (getter=isHousebroken, setter=setHousebrokenness:) BOOL housebroken;
This tells the compiler that even though the property is named ‘housebroken’, the value is retrieved by using the method -isHousebroken
(rather than -housebroken
), and the value is changed with -setHousebrokenness:
(rather than -setHousebroken:). Assuming you have a puppy object, you can get its housebroken state:
if ([puppy isHousebroken]) ... do stuff
And change it:
[puppy setHousebrokenness: YES];
You can also use dot notation using the property name:
puppy.housebroken = YES;
Recall that dot notation is purely syntactic sugar over a regular objective message send. The compiler sees you’re using the housebroken property. It also knows that the setter is called -setHousebrokenness:
, so the compiler actually emits this:
[puppy setHousebrokenness: YES];
iOS developers see the keyword nonatomic
all the time. It’s part of the @property
incantation and dance – if you create a property, make it nonatomic
. But what does that mean? It’s the opposite of atomic
. (duh)
But what is atomic
? Does that mean it’s thread safe?
It depends on what you mean by thread safety. In a sense, an atomic property is localized thread safety – an atomic value can be changed by multiple threads without getting corrupted because reading and writing the property’s value is serialized.
Say the puppy gets a back yard where it can roam freely, and you want to store the area of the yard:
@property CGRect domain;
This property is atomic
by default. That means you won’t get garbage if multiple threads manipulate the value simultaneously. This is what a rectangle looks like in memory:
Four CGFloats stacked end-to-end. Now say you have two threads wanting to change it:
<b>thread 1:</b> puppy.domain = CGRectMake (1.0, 2.0, 3.0, 4.0);
<b>thread 2:</b> puppy.domain = CGRectMake (10.0, 20.0, 30.0, 40.0);
atomic
means that in the case of an assignment race condition you will either get this result:?
or this result:
but not scrambled values:
Nonatomic properties can lead to the last scrambled value if a value is changed in multiple threads.
But in the larger view, atomic
properties do not make your code truly thread-safe. All it means is that this one property’s value is always changed completely so that you won’t have a mixture of old and new.
It’s still possible to have a coherent domain for the puppy, but have the the rest of the puppy’s data being wrong. Say you had code in one method that sets some puppy properties:
puppy.name = @"Hoover";
puppy.domain = CGRectMake (1.0, 2.0, 3.0, 4.0);
puppy.housebroken = NO;
And another method running on another thread was doing similar work at the exact same time, but with different data:
puppy.name = @"Rumpelstiltskin";
puppy.domain = CGRectMake (10.0, 20.0, 30.0, 40.0);
puppy.housebroken = YES;
The domain is atomic, so it will always have a coherent value. But depending on how threads get scheduled you could end up with a puppy named Hoover and is not housebroken, but has a domain of { 10.0, 20.0, 30.0, 40.0 }
. The value is internally consistent, but wrong. Overall, this operation on a puppy is not thread-safe.
What about making the object property atomic? Still does not mean access to the object is thread safe. You could have a kennel club object with this declaration:
@interface KennelKlub : NSObject
@property (atomic) Puppy *alphaPuppy;
@end
This does not mean that alphaPuppy
is in any way thread safe. Only the pointer has atomic semantics. This declaration only means that there won’t be any memory scrambling if two threads try to copy over the 4 bytes of a 32-bit puppy pointer.
Making code atomic is slower than code that just copies some bytes around. You need some kind of synchronization mechanism. If you know you’re in a situation where object values must not be changed on multiple threads (such as stuff on user interface objects, or your object’s contract in general is “do not use one object on multiple threads”) you can avoid the whole synchronization step.
The general recommendation is to use atomic properties on the Mac, because the machines are so fast that a little bit of synchronization won’t be an issue. On iOS, use nonatomic properties to reduce the total amount of work done, freeing up limited CPU for other uses (as well as being nicer on the battery.)
Atomic is the default, so you can omit the attribute if you wish, but you’re welcome to use the atomic keyword if you want to explicitly state “yes, this property is atomic.” Early versions of clang didn’t support the atomic
keyword, but it was added sometime during Xcode 4’s life.
Most properties have a corresponding instance variable that stores the value for the property. A Puppy object should contain an NSString
pointer for the puppy’s name and a BOOL for the housebroken status. Where does this instance variable come from, and how can it be used?
If you do absolutely nothing and just have a @property
statement in your interface or class extension, the compiler will add an instance variable to your class with the property name prepended by an underscore. In this case, we’d automatically get _puppyName
and _housebroken
. Why prepend the underscore? It prevents a particular class of bugs. The compiler will also emit object code for the setter and getter method.
You can supply your own setter or getter method (or both) if you choose. The compiler will emit an implementation for either method if it’s not provided in the source code. For example:
@property (nonatomic) NSInteger split;
...
- (NSInteger) split {
NSLog (@"Someone called split! Value is %zd", _split);
return _split;
}
You supplied the getter. The compiler will emit the setter, as well as add the backing instance variable _split
.
There’s a couple of corner cases, though.
If you supply both the setter and the getter, the compiler assumes you’re handling all the details including where to store the property’s value. The compiler won’t generate a backing instance variable for you in this case. Luckily, there are three places you can declare your own ivars. You can explicitly @synthesize them. Be sure to honor the contract you declare in the property attributes in your code.
Atomic properties add a wrinkle when you’re implementing your own accessors. When the compiler emits code to implement the setter and getter it looks to see if the property includes a nonatomic attribute:
@property (nonatomic) CGRect domain;
The compiler will just emit code to move sizeof(CGRect)
bytes from one place to another rather than try synchronizing the data. But if you have an atomic property:
@property CGRect domain;
Both the setter and the getter have to agree on the synchronization mechanism. If you have an atomic property and only supply one of the setter or getter, the compiler will issue some warnings (and you are fixing your warnings, right?) because it has no idea how you’re actually doing the synchronization.
puppeh.m:38:1: warning: writable atomic property 'domain' cannot pair a synthesized setter
with a user defined getter [-Watomic-property-with-user-defined-accessor]
- (CGRect) domain {
^
puppeh.m:38:1: note: setter and getter must both be synthesized, or both be user defined, or the
property must be nonatomic
puppeh.m:14:30: note: property declared here
@property CGRect domain;
Properties can be read-only. You might have some attribute on your object that is actually a calculated value. Say the puppy has a first name and last name, and the full name is a concatenation of the two:
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, readonly) NSString *puppyName;
...
- (NSString *) puppyName {
return [NSString stringWithFormat: @"%@ %@", self.firstName, self.lastName];
}
By using the readonly property attribute you’re telling the compiler to only advertise the -puppyName
method. The compiler will look at you funny if you try to call setPuppyName:
. This means that users of the class can retrieve the value, but can’t change it directly.
Is there space wasted on an auto-synthesized instance variable for puppyName
? Nope. The compiler is smart enough to know that when you explicitly provide an accessor method for a readonly property there’s no need for it to create the instance variable. Its just an application of the earlier rule about when the compiler auto-synthesizes a backing ivar.
You can declare a property as read-only in the public interface of the class, and re-declare it as readwrite in a class extension. This lets you use compiler-generated atomic setters and getters inside of your implementation, but not expose the setters to the rest of the world. Check out Read/Write All About It for more details.
Cocoa has a plethora of options regarding memory handling. Non-object values should be byte-wise copied. Object pointers can be strong
, which means pointed-to objects are to be kept alive. You can have weak
object pointers that don’t keep other objects alive, and will be back-filled with zeros if the pointed-to object gets destroyed. You can copy
objects and not even reference the original any more. You can also have object pointers that just byte-wise copy and do no memory management.
You should always use copy for NSString
attributes. The motivation for this can be found in About Mutability, in the section “Mutable Stripping”.
Without any decorations, memory management defaults to strong
in properties under ARC.
Explicitly declared instance variables used with properties have to match memory management types, otherwise you’ll get a compiler error. Here is an (implicitly) strong instance variable that will get used by a weak property:
@interface Puppy : NSObject {
NSString *_favoriteFood;
}
@property (weak) NSString * favoriteFood;
@end
Yields the nice message:
error: existing instance variable '_favoriteFood' for __weak property 'favoriteFood' must be __weak
puppeh.m:22:35: note: property declared here
@property (weak) NSString *favoriteFood
^
This is because instance variables are strong
by default. Why is this a hard error and not just a warning? Using mixed memory management concepts for a single property could easily lead to confusion inside of the implementation file if you mixed and matched direct-ivar access (strong pointers) and vectoring through the property methods (weak pointers). So either don’t explicitly declare the ivar, or prepend it with __weak
.
Delegates and parent pointers should be weak references so you avoid retain cycles.
There are some times where you want assign
semantics on a pointer, such as referencing one of the few classes that can’t have weak references to them. You can use assign, or use the more semantically-obvious unsafe_unretained
attribute. You can see more about unsafe_unretained at Unsafe at Any Speed.
If you’re only using ARC, you can skip this section.
Non-ARC memory management doesn’t use strong and weak, but instead uses retain
and assign
attributes for similar things. You can still use copy
, of course. retain
means that the property will be retained when assigned and released when replaced. assign
works like you saw before – the bytes for the pointer are just copied, like with unsafe_unretained. These are not weak references though. If an assigned object is destroyed, anybody pointing to that object now has a dangling reference. A new object being created at that address can cause all sorts of strange bugs. The Zombies instruments template can help track down these cases.
Here’s a quick list of all of the different property attributes:
<b>API Control</b>
getter=methodname
setter=methodname:
<b>Read / Write Serialization</b> (not general thread safety)
nonatomic
atomic (default)
<b>Mutability</b>
readonly
readwrite (default)
<b>Memory Management (ARC)</b>
copy
strong (default)
weak
unsafe_unretained
assign
<b>Memory Management (Traditional)</b>
copy
retain
assign (default)
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...