a tale of two `self`
A while ago, I came across a problematic piece of code that turned out to be an interesting one:
extension UIViewController {
static var screenName = String(describing: self)
}
The value of screenName is always equal to (Function). But what I needed was the type of UIViewController instance as a String.
For example:
class ReaderViewController: UIViewController { ... }
print(ReaderViewController)
// Expected Output: ReaderViewController
// Actual Output: (Function)
But why is that so?
I tried to simplify the problematic piece of code and reduce the variables that were involved. In the first step, I took out screenName from the extension and put it inside a class:
class ReaderViewController: UIViewController {
static var screenName = String(describing: self)
}
The result was the same.
What if ReaderViewController doesn’t inherit from UIViewController?
class ReaderViewController {
static var screenName = String(describing: self)
}
Boom! Compiler Error:
Use of unresolved identifier 'self'.
By trial and error I managed to narrow down the problem to the point that led me to this conclusion:
- If the root class of
ReaderViewControllerisNSObject, the value ofscreenNameis(Function). - Otherwise, we get the compiler error
It looks like we have two different problems here that need to be investigated separately. But actually, when I was looking for the root cause of the former one, I found an explanation for the later one too.
When I jumped to the definition of NSObject, I faced this (summarized) code:
public protocol NSObjectProtocol {
//...
func `self`() -> Self
//...
}
@available(iOS 2.0, *)
open class NSObject : NSObjectProtocol {
//...
}
self is a method that returns the object which it is called on.
It explains why String(describing: self) returns (Function) within a subclass of NSObject. But it doesn’t explain why within the initializer expression of a stored type property, self isn’t available.
I replaced the stored type property with a computed one, and it silenced the compiler-error problem:
class ReaderViewController {
static var screenName: String {
return String(describing: self)
}
}
Still, I don’t know why referring to self within type property initializer expression caused that compiler error and I have asked it here.
By the way, at least now I know that self within type property initializer expression is not available, so it is an unresolved identifier. If the class is a subclass of NSObject, in the absence of self, NSObject’s self ( which is a method ) is available and String(describing: self) returns the type of it which is (Function).
P.S. While I was writing this post, I found this related question on Swift Forum:
Do you have question, suggestion, feedback? Hit me on twitter: @coybit🙂