Avoiding collisions with future class changes

Ages ago, I had an Objective-C application crash on a new version of OS X. The issue was that my subclass of an Apple class had a property, but Apple added a property with the same name. So to avoid this issue in the future, I started to prefix/suffix all my code to guarantee uniqueness.


Fast-forward to Swift. When starting out, I kept the suffix scheme going. A contrived example:


class IIMainViewController : UITableViewController { // custom type prefix
    var refreshControl_II : UIControl!     // custom property suffix


If I drop the _II suffix, I'll get an appropriate error: "Property 'refreshControl' with type 'UIControl?' cannot override a property with type 'UIRefreshControl?'


But let's pretend that refreshControl wouldn't have been added to UITableViewController until iOS 13. What would happen with a Swift app with a 'refreshControl' property in the table view subclass when running on iOS 13?


In all the namespacing articles I've read, I haven't come accross this specific example.

Replies

Not sure, just trying to use what I learned with debugger…


The error you get is at compile time, not runtime.


Once compiled, your property gets an internal name (with compiler generated prefix and suffix and even midfix).


There is no risk of clash.


If a refreshControl_II property is added in IOS 18, there will be no conflict at run time.

But when you recompile, you'll get an error.


Hope I'm right.

Yes, the above example shows a compile-time error. Again, let's pretend that's not the case and that 'refreshControl' didn't exist in UITableViewController until iOS 13. So an app compiled against the iOS 12 SDK and on user's devices... what happens after they update to iOS 13.


Do you have a source of documentation for "Once compiled, your property gets an internal name"? If true, then I agree I'll be OK. Apparently though, in Objective-C, no name mangling was done; hence the crash.

Have a look at compiler generated reports, with mangled names:


https://forums.developer.apple.com/thread/109097


Here is a crash report from one of my apps (I have hidden some names):


for a func

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread

0 com.XXXXXX.StartYYYYYY 0x0000000109327637 _TFC11YYYYYYY19EdTSingleController27setReservoirSliderMaxCValuefT_T_ + 6775


for parameters names : forceProprio becomes Si12forceProprio

25 com.XXXXXX.StartYYYYYY 0x0000000109468032 _TF11YYYYYY16openOneSingleFT6numEdSi10itemToOpenSi12forceProprioSb15reuseControllerSb6ofTypeGSqOS_6CasEd__T_ + 4770

While Claude31 is correct that Swift uses prefixes and name mangling to avoid many conflicts, that’s not what’s happening with your

refreshControl
property example. Consider this test:
class IIMainViewController : UITableViewController {
    @objc var refreshControl2: UIControl!
    var refreshControl3: UIControl!
}

func printPropertiesOfClass(_ thisClass: AnyClass, indent: Int = 0) {
    let pad = String(repeating: " ", count: indent)
    print("\(pad)\(NSStringFromClass(thisClass)):")
    var propertyCount: UInt32 = 0
    if let propertiesBase = class_copyPropertyList(thisClass, &propertyCount) {
        let properties = UnsafeBufferPointer(start: propertiesBase, count: Int(propertyCount))
        for property in properties {
            let name = String(cString: property_getName(property))
            print("  \(pad)\(name)")
        }
        free(propertiesBase)
    }
    if let superClass = class_getSuperclass(thisClass) {
        printPropertiesOfClass(superClass, indent: indent + 2)
    }
}

printPropertiesOfClass(IIMainViewController.self)

This uses the Objective-C runtime to enumerate all the properties of a class and its super classes. On my machine this prints:

MyTestApp.IIMainViewController:
  refreshControl2
  UITableViewController:
    …
    refreshControl
    …
    UIViewController:
      …
      UIResponder:
        …
        NSObject:
          …

There’s three interesting things to note here:

  • The class name,

    IIMainViewController
    , won’t conflict with other Objective-C classes because it has the module name as the prefix.
  • The

    refreshControl2
    property won’t conflict with other Objective-C properties because it’s not exported to Objective-C at all.
  • If you export it to Objective-C, using the

    @objc
    attribute, as shown by
    refreshControl3
    , you do have to worry about conflicts.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you, Quinn.


I spent some time switching my code between the strict-naming and non-strict-naming schemes. Along with risk outlined above, I also found that when using non-strict naming, code was more difficult to reason about. i.e. you had to know at the time of writing if something could collide.


Thus, I decided to leave the strict-naming in place and just both prefix and suffix everything. While extreme, this guarantees no collisions ever. Also no shadowing of any kind. At a glance, I can identify any of my code in the editor* as well as when doing code-completion.


*Xcode does have syntax colorization where you can set a unique color for your own types/code, but I find it's also nice to have a textual difference too.