同时重写了setter和getter方法,@property不会自动生成(成员变量/实例变量)
成都创新互联公司-专业网站定制、快速模板网站建设、高性价比龙泉驿网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式龙泉驿网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖龙泉驿地区。费用合理售后完善,十余年实体公司更值得信赖。
@property的作用是定义属性,声明getter,setter方法。(注意:属性不是变量)
@synthesize的作用是实现属性的,如getter,setter方法.
在声明属性的情况下如果重写setter,getter,方法,就需要把未识别的变量在@synthesize中定义,把属性的存取方法作用于变量。如:
.h文件中
后来因为使用@property灰常频繁,就简略了@synthesize的表达。
从Xcode4.4以后@property已经独揽了@synthesize的功能主要有三个作用:
(1)生成了私有的带下划线的的成员变量因此子类不可以直接访问,但是可以通过get/set方法访问。那么如果想让定义的成员变量让子类直接访问那么只能在.h文件中定义成员变量了,因为它默认是@protected
(2)生成了get/set方法的实现
当:
用@property声明的成员属性,相当于自动生成了setter getter方法,如果重写了set和get方法,与@property声明的成员属性就不是一个成员属性了,是另外一个实例变量,而这个实例变量需要手动声明。所以会报错误。
总结:一定要分清属性和变量的区别,不能混淆。@synthesize 声明的属性=变量。意思是,将属性的setter,getter方法,作用于这个变量。
@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
在Xcode4.5和以后的版本中,可以省略@synthesize,编译器会自动加上setter和getter方法的实现。并且默认会去访问_age这个成员变量,如果找不到_age这个成员变量,会自动生成一个叫做_age的私有成员变量。
@synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
[链接]( ;request_id=163548685316780261954394biz_id=0utm_medium=distribute.pc_search_result.none-task-blog-2 all sobaiduend~default-2-84540175.first_rank_v2_pc_rank_v29utm_term=synthesizespm=1018.2226.3001.4187)
在开发的过程中,对一个NSMutableDictionary(可变字典)设置键值对的时候,一般会采用setObject这个方法,但是在项目中,去云捕上看到的用户出错,好多都是报这样的错误。
很明显就是在setObject方法中插入了空值而导致的报错。解决的办法是将setObject替换成setValue就可以了。虽然解决了,但是他们之间的区别以及为什么setValue就可以呢?
因为setObject方法中,值必须是不为空的任意类型(nonnull),而setValue方法中,值可以为空值(nullable),并且在setValue方法中,假如你传的值为空的话,那么他会调用- (void)removeObjectForKey:(id)aKey;这个方法。所以用setValue方法可以有效的防止值为空所出现的炸裂现象。
注意:在系统给出的方法中,我们可以明显的看到,setValue方法中,key必须是不为空的字符串(nonnull NSString),而setObject方法中,可以传入不为空的id类型。
setValue和setObject的一些拓展�:
1.setObject方法是NSDictionary这个类中特有的,而setValue是KVC(key-value-coding 键值编码)中的主要方法。
2.setObject方法中,虽然值不能为nil,但是可以为[NSNull null],并且key可以为NSString, NSNumber等类型。
3.当 setValue:forKey:方法调用者是对象的时候:
setValue:forKey:方法是在NSObject对象中创建的,也就是说所有的oc对象都有这个方法,所以可以用于任何类。
比如使用一个person类:
Person *person = [[Person alloc] init];
[person setValue:self forKey:@"delegate"];
表示的意思是:对象person设置他的delegate属性的值为当前类,当然调用此方法的对象必须要有delegate属性才能设置,不然调用了也没效果。(这条目前还不太理解,希望有看到的大神指教)
setValue和setObject参考链接:
赋值的 时候调用set方法,如self.age = 18;//在等号左边,即左值时调用
取值的时候调用get方法,如age = self.age;//等号右边或者运算、输出时
注意_age = 18 不会调用set方法
对已经学会的知识,不断地总结,可以让你变得更强的。
什么情况下会调用kvo。
什么情况下不会调用kvo。
kvo的底层原理实现。
我们对name,哪些会真正的触发kvo呢
我可以告诉你,1 2 4 6会监听到kvo的变化,而其他的则检测不到。
又比如下面这个例子。
当我们对animal进行kvo监听,然后修改name的值,是否会被kvo监听到。
当我们对personclass对象重新赋值一个AnimalClass对象,是否会被kvo监听到。
我可以告诉你,第一种情况是监听不到的,第二种情况是可以监听到的。
然后,为什么呢?
我这里有两张图片。
当我们对一个对象进行kvo监听的时候,会生成一个NSKVONotifying_前缀的类,然后我们实际的操作是对这个类进行的。
通俗的讲,对对象的进行kvo监听后,这个对象的isa指针已经指向了NSKVONotifying_前缀的类,NSKVONotifying_Person。这个类是person的子类,他的superclass就是person类对象。
我们在调用setage方法的时候,会根据对象的isa找到NSKVONotifying_Person,然后在他的类里面找setage的实现。
那么有没有疑惑,setage中到底做了什么操作呢?
_NSsetIntValueAndNotify记住这个函数。
我们可以这样打印出setage的方法实现。
可以打印出
这个_NSsetIntValueAndNotify方法就是setage的实现,具体的实现,请再往后看。
Foundation框架中还有很多例如_NSSetBoolValueAndNotify、_NSSetCharValueAndNotify、_NSSetFloatValueAndNotify、_NSSetLongValueAndNotify等等函数。
继续往下看。
打印结果如下
在kvo监听下,包含了四个方法,setAge: class dealloc _isKVOA
我们先从clas来进行入手。
打印结果都是person。为什么呢?
进行kvo监听之后,不是进行变为NSKVONotifyin_Person这个类吗?怎么打印出来都是person。
其实是苹果不希望将这个NSKVONotifyin_Person暴露出来。然后在类的内部,重写了clas方法。
setAge方法的再深一层的内部实现?
我相信看完都会有所收获。
看完这些最开始的例子,我觉得会懂了。
我们来看下kvc的原理:
触发kvc的set方法才可以触发kvo机制。
所以这两种情况
在程序设计中,我们经常会使用 懒加载 ,顾名思义,就是用到的时候再开辟空间,比如iOS开发中的最常用控件UITableView,实现数据源方法的时候,通常我们都会这样写Objective-C- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.dataArray.count; }- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //1.得到cell XWShopCell *cell = [XWShopCell cellWithTableView:tableView]; //2.传递模型 cell.wine = self.dataArray[indexPath.row]; //3.回传cell return cell; }上面的的代码中return self.dataArray.count;其实就是利用@property (nonatomic, strong) NSArray *dataArray;@property 的特性,为属性生成了get和set方法,而这里是调用的get方法,但是上述代码中return self.dataArray.count 会调用- (NSArray *)dataArray{ return _dataArray}这样调用,如果成员属性dataArray 开始没有赋值的,那么在使用的时候,调用get方法,不重写的话,会报错,空指针,所以一般我们会重写get方法//重写get方法 - (NSArray *)dataArray { if (nil == _dataArray){ _dataArray = [NSArray array]; } return _dataArray }这样写,就防止了成员属性为没有赋值的情况综上所述,Objective-C的懒加载,其实就是调用成员属性的get方法,初始化值,而Swift的懒加载,是和Objective-C不同的Swift//MARK tablview的 dataSource 代理方法 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - Int{ return self.dataArray.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - UITableViewCell{ //1.得到cell let cell = XWShopCell.cellWithTableView(tableView) //2.传递模型 cell.wine = self.dataArray[indexPath.row] //3.回传cell return cell }而这句return self.dataArray.count在Swift 存储属性必须初始化,确认类型,或者用可选类型,总之要确认类型,毕竟Swfit是类型安全语言,所以Swift提出了lazy属性,用法//1.分析 NSArray 是一个闭包的返回值,而这是一个没有参数的闭包 lazy var dataArray:NSArray = { [] }() //2.也可以写成这样 lazy var dataArray:NSArray = { return NSArray() }()//3.从plist文件加载 lazy var dataArray:ArrayXWWine = { let winePath = NSBundle.mainBundle().pathForResource("wine.plist", ofType: nil)! let winesM = NSMutableArray(contentsOfFile: winePath); var tmpArray:ArrayXWWine! = [] for tmpWineDict in winesM! { var wine:XWWine = XWWine.wineWithDict(tmpWineDict as! NSDictionary) tmpArray.append(wine) } print("我就运行一次") return tmpArray }()上述的代码,有点难理解,如果之前会Objective-C的block 或者对C语言的函数指针理解透彻的,可以看成是一个代码块,然后self.dataArray的时候,就执行了代码块,但是重复调用,Lazy 属性的代码块只会调用一次,lazy修饰的是一个存储属性,而存放的是闭包,我想内部,应该进行了优化。