# iOS 开发常见问题汇总
# _weakSelf使用场景
# @property 和@synthesize 和 @dynamic
@property 的本质:
@property = ivar(实例变量) + getter/setter(存取方法);
在 iOS5 之后,更换为 LLVM 之后,编译器在编译过程中发现没有新的实例变量后,就会生成一个下划线开头的实例变量。因此现在我们不必在声明一个实例变量。(注意:==是不必要,不是不可以==) 当然我们也熟知,@property 声明的属性不仅仅默认给我们生成一个_类型的成员变量,同时也会生成 setter/getter 方法。在.m 文件中,编译器也会自动的生成一个成员变量_myString。
那么在.m文件中可以直接的使用_myString成员变量,也可以通过属性self.myString.都是一样的
。注意这里的 self.myString 其实是调用的 myString 属性的 setter/getter 方法
此外,如果我们再最新的代码中声明一个成员变量,如下代码所示,那么我们只是声明了一个成员变量,并没有 setter/getter 方法。所以访问成员变量时,可以直接访问 name,也可以像 C++一样用 self->name 来访问,但绝对不能用 self.name 来访问(因为成员变量默认不存在 getter 和 setter 方法)。
@interface MyViewController :UIViewController
{
NSString *name;
}
@end
@synthesize
@synthesize
表示如果属性没有手动实现 setter
和 getter
方法,编译器会自动加上这两个方法;
使用场景
如果你已经手动实现了
setter/getter
方法,或着对 只读对象 实现了getter
方法,那么自动合成不会产生任何影响。如果你在手动实现需要一个变量,只需声明它就可以了,不需要添加@synthesize
来添加一个别名(尽管可以)。当在
protocol
中声明并实现属性时。协议中声明的属性不会自动生成 setter 和 getter,[UIApplicationDelegate window]
就是个典型的例子;给变量设置别名: 例如我可以这样写@synthesize age = myAge;,那这样子的话我们去调用的时候 self.age 其实是操作的实例变量 myAge,而不是_age 了。此时使用自动生成的实例变量名将报错,只能使用指定的别名
@dynamic
@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。假如一个属性被声明为 @dynamic var,而且你没有提供 @setter 方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
# 属性引用 self.xx 与_xx 的区别
本质上: self.xx 是调用的 xx 属性的 get/set 方法,而_xx 则只是使用成员变量_xx,并不会调用 get/set 方法
两者的更深层次的区别在于,通过存取方法访问比直接访问多做了一些其他的事情(例如内存管理,复制值等),例如如果属性在@property 中属性的修饰符有 retain,那么当使用 self.xx 的时候相应的属性的引用计数器由于生成了 setter 方法而进行加 1 操作,此时的 retaincount 为 2。
比较出错的点: 最容易出问题的地方就是对属性 xx 或成员变量_xx 的初始化的地方和调用时机
例子 1
做懒加载的时候,我们将属性和实例变量的初始化放在重写的 get 方法中,于是我们在
- (void)viewDidLoad
中使用\_invoiceInfoImageView
来进行布局时,实际上因为在这之前也没有调用invoiceInfoImageView
的 get 方法,所以此时invoiceInfoImageView
的值其实为 nil,界面上是空白的。
#import "InvoiceTitleInfoViewController.h"
@interface InvoiceTitleInfoViewController ()
//定义属性invoiceInfoImageView
@property (strong, nonatomic) UIImageView *invoiceInfoImageView;
@end
@implementation InvoiceTitleInfoViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发票抬头";
//用_xx来调用实例变量_invoiceInfoImageView,此时由于没有调用get方法进行初始化,因此此时_invoiceInfoImageView的值为nil
[self.view addSubview:_invoiceInfoImageView];
WEAKSELF
[_invoiceInfoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(weakSelf.view).mas_offset(0.0f);
make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
make.right.bottom.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
}];
}
//初始化属性invoiceInfoImageView,其实这就是invoiceInfoImageView的get方法
- (UIImageView *)invoiceInfoImageView{
if (!_invoiceInfoImageView) {
_invoiceInfoImageView = [[UIImageView alloc] init];
_invoiceInfoImageView.image = [UIImage imageNamed:@"invoice_title_info"];
}
return _invoiceInfoImageView;
}
例子 2
如果我们在 使用
self.xx
来调用变量,则会调用invoiceInfoImageView
的 get 方法,进行初始化,界面布局将会显示我们想要的图片。此外,如果我们再使用\_xx
之前用self.xx
调用过变量invoiceInfoImageView
,则同样会调用其 get 方法从而触发invoiceInfoImageView
的初始化,这样也不会影响布局
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发票抬头";
//先用self.invoiceInfoImageView触发get方法进行初始化,这样_invoiceInfoImageView的值被初始化后不为nil
self.invoiceInfoImageView;
[self.view addSubview:_invoiceInfoImageView];
WEAKSELF
[_invoiceInfoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(weakSelf.view).mas_offset(0.0f);
make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
make.right.bottom.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
}];
}
同时手动重写了一个属性的get和set方法的话,Xcode不会再自动生成带有下划线的私有成员变量了
# OC 中 覆盖父类属性会有 Auto property synthesis will not synthesize property 'xxx'的警告
根本原因,protocal 中声明的熟悉,默认不自动生成 getter 和 setter,需要使用 @synthesize var = _var 声明属性,以保证存在 getter 和 setter
# 参考
- 本文链接: https://mrgaogang.github.io/ios/question/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!