会不断地占用内存
,而const定义的常量存储在数据段只有一份copy
,效率更高都
可以使用,可以
修饰对象,也可以
修饰基本数据类型只能
在ARC模式下使用,只能
修饰对象(NSString),不能
修饰基本数据类型typedef NS_ENUM(NSInteger, CYLSex)
{
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject<NSCopying>
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readwrite) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
@end
weak
关键字,相比assign
有什么不同?weak
关键字?野指针
,如果这时候在给此对象发送消息,很容造成程序奔溃为了搞清属性是怎么实现的,反编译相关的代码,他大致生成了五个东西
// 该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远
OBJC_IVAR_$类名$属性名称
// 方法对应的实现函数
setter与getter
// 成员变量列表
ivar_list
// 方法列表
method_list
// 属性列表
prop_list
只会生成
setter和getter方法声明
,我们使用属性的目的,是希望遵守我协议的对象能实现该属性只会生成
setter和getter方法声明
,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数objc_setAssociatedObject
objc_getAssociatedObject
@property (nonatomic, getter=isOn) BOOL on;
// setter=<name>这种不常用,也**不推荐**使用。故不在这里给出写法
不是
线程安全的,需要用互斥锁来保证线程安全性默认
的就是@syntheszie var = _var;自动
为你加上这两个方法用户自己实现
,不自动生成(当然对于readonly的属性只需提供getter即可)没有
提供@setter方法和@getter方法,编译的时候没问题
,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = instance.var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定
@synthesize合成成员变量的规则,有以下几点:
指定
了成员变量的名称,会生成一个指定
的名称的成员变量如果指定@synthesize foo;
就会生成一个名称为foo的成员变量,也就是说:会自动生成一个属性同名的成员变量
@interface XMGPerson : NSObject
@property (nonatomic, assign) int age;
@end
@implementation XMGPerson
// 不加这语句默认生成的成员变量名为_age
// 如果加上这一句就会生成一个跟属性名同名的成员变量
@synthesize age;
@end
如果是 @synthesize foo = _foo; 就不会生成成员变量了
不会
autosynthesis(自动合成)必须
使用@synthesize来手动合成ivar不建议
这么做,建议使用系统自动生成的成员变量可变类型
:NSMutableString、NSMutableArray、NSMutableDictionary,为确保对象中的属性值不会无意间变动,应该在设置新属性值时拷贝一份,保护其封装性写不写都行
:对于 block 使用 copy 还是 strong 效果是一样的,但是建议写上copy,因为这样显示告知调用者“编译器会自动对 block 进行了 copy 操作”浅复制(shallow copy)
:在浅复制操作时,对于被复制对象的每一层都是指针复制
。深复制(one-level-deep
copy):在深复制操作时,对于被复制对象,至少有一层
是深复制。完全复制(real-deep
copy):在完全复制操作时,对于被复制对象的每一层都是对象复制
。
非集合
类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //深复制
[可变对象 copy] //深复制
[可变对象 mutableCopy] //深复制
集合
类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //单层深复制
[可变对象 copy] //单层深复制
[可变对象 mutableCopy] //单层深复制
集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制
性能问题
,通过声明nonatomic可以节省这些虽然很小但是不必要额外开销,在iOS开发中应该使用nonatomic替代atomic
分为可变版本与不可变版本
,那么就要同时实现
NSCopyiog与NSMutableCopying协议,不过一般没什么必要,实现NSCopying协议就够了// 实现不可变版本拷贝
- (id)copyWithZone:(NSZone *)zone;
// 实现可变版本拷贝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重写带 copy 关键字的 setter
- (void)setName:(NSString *)name
{
_name = [name copy];
}
仅调用一次
,调用的顺序:父类优先于子类, 子类优先于分类不会
被类自动继承懒加载
可选
的,且只有在实现了
它们时才会被调用
只会被调用一次
Foundation对象 和 Core Foundation对象间的转换:俗称桥接
ARC环境桥接关键字:
// 可用于Foundation对象 和 Core Foundation对象间的转换
__bridge
// 用于Foundation对象 转成 Core Foundation对象
__bridge_retained
// Core Foundation对象 转成 Foundation对象
__bridge_transfer
Foundation对象 转成 Core Foundation对象
使用__bridge
桥接
__bridge
桥接,它仅仅
是将strOC的地址
给了strC, 并没有
转移对象的所有权,也就是说, 如果使用__bridge桥接, 那么如果strOC释放了,strC也不能用了可以不用主动释放
, 因为ARC会自动管理strOC和strCNSString *strOC1 = [NSString stringWithFormat:@"abcdefg"];
CFStringRef strC1 = (__bridge CFStringRef)strOC1;
NSLog(@"%@ %@", strOC1, strC1);
使用__bridge_retained
桥接
所有权转移
给strC, 也就是说, 即便strOC被释放了, strC也可以使用
必须自己手动释放
,因为桥接的时候已经将对象的所有权转移给了strC,而C语言的东西不是不归ARC管理的NSString *strOC2 = [NSString stringWithFormat:@"abcdefg"];
// CFStringRef strC2 = (__bridge_retained CFStringRef)strOC2;
CFStringRef strC2 = CFBridgingRetain(strOC2);// 这一句, 就等同于上一句
CFRelease(strC2);
Core Foundation对象 转成 Foundation对象
使用__bridge桥接
仅仅
是将strC的地址
给了strOC, 并没有
转移对象的所有权CFStringRef strC3 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
NSString *strOC3 = (__bridge NSString *)strC3;
CFRelease(strC3);
使用__bridge_transfer桥接
所有权转移
给strOC, 也就是说, 即便strC被释放了, strOC也可以使用
不用手动释放strC
CFStringRef strC4 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
// NSString *strOC = (__bridge_transfer NSString *)strC;
NSString *strOC4 = CFBridgingRelease(strC4); // 这一句, 就等同于上一句
MRC环境:直接强转
-(void)bridgeInMRC
{
// 将Foundation对象转换为Core Foundation对象,直接强制类型转换即可
NSString *strOC1 = [NSString stringWithFormat:@"xxxxxx"];
CFStringRef strC1 = (CFStringRef)strOC1;
NSLog(@"%@ %@", strOC1, strC1);
[strOC1 release];
CFRelease(strC1);
// 将Core Foundation对象转换为Foundation对象,直接强制类型转换即可
CFStringRef strC2 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
NSString *strOC2 = (NSString *)strC2;
NSLog(@"%@ %@", strOC2, strC2);
[strOC2 release];
CFRelease(strC2);
}
/**
1. self.person:要监听的对象
2. 参数说明
1> 观察者,负责处理监听事件的对象
2> 要监听的属性
3> 观察的选项(观察新、旧值,也可以都观察)
4> 上下文,用于传递数据,可以利用上下文区分不同的监听
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
* 当监控的某个属性的值改变了就会调用
*
* @param keyPath 监听的属性名
* @param object 属性所属的对象
* @param change 属性的修改情况(属性原来的值、属性最新的值)
* @param context 传递的上下文数据,与监听的时候传递的一致,可以利用上下文区分不同的监听
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性改变了:%@", object, keyPath, change);
}
第一次被观察
时,系统就会在运行期动态
地创建该类的一个派生类
,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
NSKVONotifying_Person
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// “手动触发self.now的KVO”,必写。
[self willChangeValueForKey:@"now"];
// “手动触发self.now的KVO”,必写。
[self didChangeValueForKey:@"now"];
}
必须
用在集合对象
上或普通对象的集合属性
上