Objective-C中同时声明属性与实例变量有何优势?
嘿,这个问题问得特别实在——确实,在Objective-C里声明@property时,LLVM编译器默认会自动生成对应的下划线开头实例变量(比如_propertyName),那为啥还要多此一举在实现里手动定义实例变量呢?下面就拆解下这么做的几个实用场景和优势:
自定义实例变量的名称与存储特性
默认自动合成的实例变量是下划线前缀,但如果你想换个命名风格(比如不用下划线,或者自定义更具语义的名字),手动声明就能直接搞定。比如:// 接口文件 @interface User : NSObject @property (nonatomic, strong) NSString *username; @end // 实现文件 @implementation User { NSString *_userAccount; // 自定义实例变量名 } // 手动实现getter/setter关联到这个变量 - (NSString *)username { return _userAccount; } - (void)setUsername:(NSString *)username { _userAccount = username; } @end另外,你还能直接给实例变量指定存储修饰符(比如
__weak、__unsafe_unretained),在一些特殊内存管理场景下,能实现比@property属性更精细的控制。兼容老旧编译环境或特殊项目设置
虽然现在Xcode默认用的LLVM编译器支持自动合成实例变量,但在一些非常老的项目(比如用GCC编译的)或者关闭了自动合成开关的工程里,必须手动声明实例变量,否则@property会找不到对应的存储容器。维护遗留代码时,这种情况还是可能碰到的。在Category中模拟属性存储
Objective-C的Category本身不能直接给类添加实例变量,但如果在Category的实现里手动声明静态实例变量(比如用字典存储关联值),再配合@property的getter/setter实现,就能模拟出属性的效果。比如:// Category接口 @interface UIViewController (Toast) @property (nonatomic, copy) NSString *toastMessage; @end // Category实现 @implementation UIViewController (Toast) { static NSMutableDictionary *_toastStorage; } + (void)initialize { if (self == [UIViewController class]) { _toastStorage = [[NSMutableDictionary alloc] init]; } } - (NSString *)toastMessage { return _toastStorage[self]; } - (void)setToastMessage:(NSString *)toastMessage { _toastStorage[self] = toastMessage; } @end现在更常用关联对象(
objc_setAssociatedObject),但手动声明实例变量是早期实现这类需求的经典方式。避免自动合成的意外缺失
当你手动实现了@property的getter和setter全部方法时,编译器会默认停止自动合成对应的实例变量。这时候如果你还需要用实例变量存储值,就必须手动声明,否则编译时会报错找不到变量。比如:@interface MyClass : NSObject @property (nonatomic, strong) NSString *name; @end @implementation MyClass // 手动实现了完整的getter和setter - (NSString *)name { // 这里加了自定义日志逻辑 NSLog(@"获取name值"); return _name; // 没手动声明的话,这里会编译报错 } - (void)setName:(NSString *)name { // 自定义赋值逻辑 if (name.length > 0) { _name = name; } } // 必须手动声明实例变量 { NSString *_name; } @end提升代码可读性与调试便捷性
手动声明实例变量能让类的存储结构一目了然,团队协作时,其他开发者不用去猜@property对应的下划线变量名。另外,在调试过程中,手动声明的变量在调试器里的显示可能更直观(虽然现在编译器优化后差别不大,但这是个习惯层面的优势)。
内容的提问来源于stack exchange,提问作者iOScoder

