TL;DR The way you are logging can affect the results, you might get a temporary ownership when logging, therefore increasing all your printing results by 1.
The following answer is a simplification because you shouldn't really worry about the real value in retainCount.
The retain count keeps a count how many references (owners) the given object has.
When created, there is exactly one owner, therefore the retain count is set to 1. Every time the object gets a new owner (retain), the retain count is increased by one. Every time the object loses and owner (release) the retain count is decreased by one.
Note that the retainCount can never reach zero. If the number of owners is 1 and you lose the owner, the object is deallocated, the count is not decreased then.
For a better test, I have created an Obj-C class, compiled without ARC:
@implementation TestObject
- (instancetype)init {
TestObject *result = [super init];
NSLog(@"Retain count after creation: %@", @(self.retainCount));
return result;
}
- (instancetype)retain {
TestObject *result = [super retain];
NSLog(@"Retain count after retain: %@", @(self.retainCount));
return result;
}
- (oneway void)release {
NSLog(@"Retain count before release: %@", @(self.retainCount));
[super release];
}
- (void)dealloc {
NSLog(@"Retain count before dealloc: %@", @(self.retainCount));
[super dealloc];
}
@end
and used it in Swift instead of your NSObject:
var foo1: TestObject? = TestObject()
print("#")
print(CFGetRetainCount(foo1))
var foo2 = foo1
print("#")
print(CFGetRetainCount(foo1))
foo2 = nil
print("#")
print(CFGetRetainCount(foo1))
foo1 = nil
Resulting in:
Retain count after creation: 1
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count after retain: 2
#
Retain count after retain: 3
3
Retain count before release: 3
Retain count before release: 2
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count before release: 1
Retain count before dealloc: 1
Which is basically what you would expect but there are additional retains and releases around every CFGetRetainCount when you get a temporary ownership when passing the object into the function.
This is one of the examples why you should never read the value of retainCount. It has no debugging value, which is also mentioned in the documentation.