Memory management in Swift for class instances is handled by Automatic Reference Counting, or ARC, which tracks the number of strong references to each instance and deallocates the instance when the count drops to zero. A retain cycle occurs when two class instances hold strong references to each other, preventing either from ever being deallocated. Retain cycles can be broken by using weak or unowned references. A weak reference is always optional and is automatically set to nil when the referenced object is deallocated, making it safe to use whenever the referenced object may eventually be released. An unowned reference is non-optional and assumes the referenced object always exists; accessing it after deallocation causes a crash, so it should only be used when the lifetime of the referenced object is guaranteed.
Properties in Swift come in several forms. Stored properties hold values directly, and computed properties calculate values through a getter and an optional setter each time they are accessed. Read-only computed properties can omit the get keyword. Property observers respond to changes in a stored property's value through willSet and didSet blocks, which run before and after the change respectively, with the upcoming value available as newValue and the previous value as oldValue. Property observers cannot be applied to computed properties, which already use getters and setters, but they can be applied to inherited stored or computed properties through overriding. A lazy stored property is one whose initial value is not computed until the first time it is accessed, which is useful for expensive initialization; lazy properties must be declared with var and are not thread-safe by default.
Swift offers several advanced features beyond these basics. Type casting allows developers to check or convert the type of an instance using the is operator for type checks, the as? operator for conditional downcasts that return an optional, and the as! operator for forced downcasts that crash on failure. Access control defines five levels of visibility: open allows access and overriding from any module, public allows access but not overriding outside the module, internal is the default and limits access to the same module, fileprivate restricts access to the same file, and private restricts access to the enclosing declaration. Finally, classes can define convenience initializers as secondary entry points that must call a designated initializer from the same class, providing additional ways to construct instances with sensible defaults.