iOS52个有效方法(二)

一.属性
1.实例变量与属性的区别之一: 实例变量是在编译期就硬编码到内存中的, 如果修改了类定义之后必须重新编译,否则就会出错! 从这里,我理解了为什么runtime**不能**为类Class新增实例变量, 却可以新增属性(实质只是新增了setter/getter方法,比如NSString增加一个 url的 NSString属性)

2.属性特质
①原子性atomic, 读写安全(本质是加了setter和getter都同步锁), 但不能保证是线程安全的(多线程访问下, 因为线程1可能连续修改set属性值, 而线程2可能在连续get属性值,get出来的值可能是不一样的)

如何手动实现原子性http://www.sohu.com/a/156876090_609543
如何理解automic http://blog.csdn.net/songchunmin_/article/details/53391970

②readonly, 用于保护网络数据的原始性, 如要修改,在.m文件中重新声明为readwrite即可修改.
③assign, 用于基本数据类型的访问, 直接访问
④weak, 访问对象类型,非拥有关系,能自动置nil(delegate)

如何实现weak特质:http://blog.csdn.net/lzhgxiaoxiong/article/details/47954687

⑤strong, 访问对象类型,拥有关系,引用计数+1
⑥copy, 访问对象类型, 分为深拷贝和浅拷贝, 一般用于NSString, NSArray和NSDictionary, block属性

1.方法copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
2.方法mutableCopy拷贝出来的对象类型总是可变类型(例如NSMutableString,NSMutableDictionary,NSMutableArray等等)
3.注意:属性特质中只有”copy”, 并无”mutableCopy”
参考https://www.jianshu.com/p/5254f1277dba


二.实例变量和属性的使用
1. 在get值时, 使用实例变量
2. 在set值时,使用属性
3. 在init方法里,使用实例变量set/get
4. 必须使用属性访问的情况: KVO监听, 懒加载等.


三.对象等同性
1.”等同性”约定是: 若两对象相等,则其哈希码(hash)一定相等, 但是两个哈希码相同的对象却不一定相等.
(如果不符合这一个准则,则说明没有正确重写isEqual和hash方法)

2.编写hash时,应该在”减少碰撞频度”与”降低运算复杂度”之间做取舍.
代码范例:

// 哈希码生成方法只要能保证两个相同的对象生成的是两个相同的
// 哈希码就行了(相同哈希码的对象不一定相等)
// 下面是一种比较好的哈希码生成方式,
//既能保持高效率,又能使生成的哈希码至少位于一定范围之内,而不会过于频繁地重复。 
-(NSUInteger)hash{
     NSUInteger firstNameHash = [_firstName hash];     
     NSUInteger lastNameHash = [_lastName hash];     
     NSUInteger ageHash = _age;     
     return firstNameHash ^ lastNameHash ^ ageHash;
} 

3.自定义类可以编写属于自己的”等同性判定方法”, 比如Person类可以写一个isEqualToPerson: 并注意需要重写isEqual方法(如果是后台数据,也可以根据数据库给的唯一id来判定等同性).

代码范例:

-(BOOL)isEqualToPerson:(EOCPerson*)otherPerson{
     // 如果指针相等,则指向同一对象,所以相等     
     if(self == object) return YES;          
     // 只有每个属性都相等,才认为两个对象相等。     
     if(![_firstName isEqualToString:otherPerson.firstName]) return NO;     
     if(![_lastName isEqualToString:otherPerson.lastName]) return NO;     
     if(_age != otherPerson.age) return NO;     
     return YES 
}  
-(BOOL)isEqual:(id)object{
     // 如果两个对象属于同一个类,则由自己的方法判定,否则交由超类判定     
     if([self class] != [object class]){
          return [self isEqualToPerson:(EOCPerson*)obj];    
      }else{    
          return [super isEqual:object];     
      } 
} 


四.以”类族模式”隐藏[抽象基类]的实现细节

类族(class cluster)是一种很有用的设计模式,可以隐藏”抽象基类”背后的实现细节,比如大部分的集合collection和UIButton. 还有,类族的”等同性”需要使用isMemberOf和isKindOf来判定,而不是isEqual方法.

范例如下:
1.使用”工厂模式”创建类族

// EOCEmployee.h 抽象基类

typedef NS_ENUM(NSUInteger, EOCEmployeeType){
     EOCEmployeeTypeDeveloper,     
     EOCEmployeeTypeDesigner,     
     EOCEmployeeTypeFinance, 
};  

@interface EOCEmployee : NSObject  
@property (copy) NSString *name; 
@property NSUInteger salary;  
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type;
- (void)doADayWork;
@end  

// EOCEmployee.m 
@implementation EOCEmployee  
// 使用工厂模式创建类簇
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type{
     // 根据传入的雇员类型参数返回不同的子类对象     
     switch (type) {     
         case EOCEmployeeTypeDeveloper:             
         return [EOCEmployeeDeveloper new];             
         break;         
         case EOCEmployeeTypeDesigner:             
         return [EOCEmployeeDesigner new];             
         break;         
         case EOCEmployeeTypeFinance:             
         return [EOCEmployeeFinance new];             
         break;         
     } 
}  
-(void)doADayWork{
     // 由子类实现, 这里可以抛出exception来警告!
} 
@end  

// EOCEmployeeTypeDeveloper枚举类型对应的子类,其它两个子类类似 
// EOCEmployeeDeveloper.h 
@interface EOCEmployeeDeveloper : EOCEmployee 
@end  
// EOCEmployeeDeveloper.m 
@implementation EOCEmployeeDeveloper 
- (void)doADayWork{
     // 具体实现代码 
} 
@end


五.使用强关联对象(属性)
有时候类的实例可能是由某种机制所创建的,而开发者无法令这种机制创建出自己所写的子类实例。之中情况就需要用关联对象来解决问题。 可以给某关联对象关联许多其他对象,这些对象通过来区分。存储对象值的时候可以指明存储策略,用以维护相应的内存管理语义。

关联类型                            等效的属性特质
OBJC_ASSOCIATION_ASSIGN             assgin
OBJC_ASSOCIATION_RETAIN_NONATOMIC   nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC     nonatomic,copy
OBJC_ASSOCIATION_RETAIN             retain
OBJC_ASSOCIATION_COPY                   copy

下列API可以管理关联对象

// 以给定的键和策略为某对象设置关联对象值 
void objc_setAssociatedObject(id object, void *key, id value, 
                              objc_AssociationPolicy policy) 

// 根据给定的键从某对象中获取关联对象值 
id objc_getAssociatedObject(id object, void *key) 

// 移除指定对象的全部关联对象
void objc_removeAssociatedObjects(id object)

下面是一个关联对象用法的示例,示例使用的是给UIAlertView类新增一个block类型的属性对象:

// 导入运行时系统框架 
#import <objc/runtime.h>  
// 定义全局静态变量做键 
static void *EOCMyAlertViewKey = "EOCMyAlertViewKey";  
- (void)askUserAQuestion{
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" message:@"What do you want to do?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Continue", nil];          
     // 定义一个处理按钮逻辑的块     
     void (^block)(NSUInteger) = ^(NSUInteger buttonIndex){      
        if (buttonIndex == 0) {        
           // 停止         
        }         
        else{        
           // 继续         
        }     
     };          
     // 将块设置为alert的关联对象     
     objc_setAssociatedObject(alert, EOCMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);          
     [alert show]; 
}  

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
     // 获取关联对象中的块   
     void (^block)(NSUInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey);  
     //执行   
     block(buttonIndex); 
}

这样做的好处是,将创建警告视图与处理操作结果的代码放到了一起,这样能增强代码的可读性。需要注意的是,块可能要捕获某些变量,这也许会造成保留环。因为关联对象之间的关系并没有正式的定义,其内存管理语句是在关联的时候才定义的,而不是接口中预先定好的。使用块作关联对象的时候一定要小心,更好的做法是将块保存为子类中的属性。

猜你喜欢

转载自blog.csdn.net/james15902085063/article/details/79637864