美文网首页
编写高质量iOS的52个有效方法学习笔记(6-12)

编写高质量iOS的52个有效方法学习笔记(6-12)

作者: 江湖闹士 | 来源:发表于2017-06-27 15:27 被阅读15次

    C语言是面向过程的语言,Objective-C语言是面向对象的语言,“对象”是基本构造单元,开发者可以通过对象来存储并传递数据。对象之间传递数据并执行任务的过程称为“消息传递”。
    当应用程序运行起来以后,为其提供相关技术支持的代码叫做“Objective-C 运行期环境”(Objective-C runtime),它提供了一些使得对象之间传递消息的重要函数,并且包含创建类实例的全部逻辑。

    6.理解“属性”这一概念

    “属性”(property)是Objective-C的一项特性,用于封装对象中的数据。

     @property (nonatomic,copy,readonly) NSString *sex;
    

    这种写法包含了getter和setter方法,使用点语法可以来调用getter和setter方法

    self.sex = @"男"; //same as:
    [self setSex:@"男"];
        
    NSString *sexStr = self.sex; //same as:
    NSString *sexStr = [self sex];
    

    属性的几个特性:原子性、读写性、语义性

    对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能得到一个完好无损的对象。
    而nonatomic就没有这个保证了。所以,nonatomic的速度要比atomic快。
    • Atomic

      是默认的
      会保证 CPU 能在别的线程来访问这个属性之前,先执 行完当前流程
      速度不快,因为要保证操作整体完成

    • Non-Atomic

      不是默认的
      更快
      线程不安全
      如有两个线程访问同一个属性,会出现无法预料的结果

    读写特性:readwrite拥有getter(获取方法)与setter(设置方法),若该属性由@synthesize实现,则编译器会自动生成这两种方法,@synthesize在.m中声明,系统默认是有的,可以不写。
    readonly(只读)仅有获取方法

    语义特性

    1,当把语义特性声明为assign时,setter和getter时方法内部实现
    
    - (void)setName:(NSString *)name{
    
    _name = name;
    
    }
    
    - (NSString *)name{
    
    return _name;
    
    }
    
    2,当把语义特性声明为retain时,setter和getter方法内部实现
    
    - (void)setName:(NSString *)name{
    
        if (_name != name) {
    
        [ _name release];
    
        _name = [name retain];
    
        }
    
    }
    - (NSString *)name{
    
        return [[ _name retain] autorelease];
    
    }
    
    3,当把语义特性声明为copy时,setter和getter方法内部实现
    
    - (void)setName:(NSString *)name{
    
        if (_name != name) {
    
        [ _name release];
    
        _name = [name copy];
    
        }
    
    }
    
    - (NSString *)name{
    
        return [[ _name retain] autorelease];
    
    }
    
    • 可以用@property语法来定义对象中所封装的数据
    • 通过“特性”来指定存储数据所需的正确语义
    • 在设置属性所对应的实例变量时,一定要遵循属性所声明的语义
    • 开发iOS程序时应该使用nonatomic属性,因为atomic属性会严重影响性能

    7、在对象内部尽量直接访问实例变量

    在对象之外访问实例变量时,总是应该通过属性来做,然而在对象内部访问实例变量时的建议是:在写入实例变量时通过其“设置方法”来做,而在读取实例变量时,则直接访问之。之所以要通过“设置方法”来写入实例变量,其首要原因在于,这样做能够确保相关属性的“语义特性”得以贯彻。
    需要注意的两种情况是:1、在初始化方法中应该直接访问实例变量,因为子类可能会“覆写”(overrede)设置方法。2、懒加载。必须通过“获取方法”来访问属性,否则,实例变量就永远不会初始化。

    以上获取方法指的就是点语法。

    • 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
    • 在初始化方法以及dealloc方法中,总是应该直接通过实例变量来读写数据
    • 使用懒加载的话必须用点语法操作。

    8、理解“对象等同性”这一概念

    - (BOOL)isEqual:(id)object {
      if ([self class] == [object class]) {
          return [self isEqualToPerson:(Person *)object];
      }else{
          return [super isEqual:object];
      }
    }
    
    - (BOOL)isEqualToPerson:(Person *)person {
      if (self == object) {
          return YES;
      }
      if (![_firstName isEqualToString:person.firstName]) {
          return NO;
      }
      if (![_lastName isEqualToString:person.lastName]) {
          return NO;
      }
      if (_age != person.age ) {
          return NO;
      }
      return YES;
    }
    
    • 若想检测对象的等同性,请提供“isEqual:”与hash 方法。
    • 相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
    • 不要盲目的逐个检测每条属性,而是应该依照具体需求来指定检测方案。
    • 编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。

    9、以“类族模式”隐藏实现细节

    “类族”是一种很有用的模式,可以隐藏“抽象基类”背后的实现细节。类方法,(工厂模式)

    • 类族模式可以把实现细节隐藏在一套简单的公共接口后面。
    • 系统框架中经常使用类族。
    • 从类族的公共抽象基类中继承子类时要当心,若有开发文档,则应首先阅读。

    10、在既有类中使用关联对象存放自定义数据

    有时需要在对象中存放相关信息、这时我们通常会从对象所属的类中继承一个子类,然后改用这个子类对象。然而并非所有情况下都能这么做,有时候类的实例可能是由某种机制创建的,而开发者无法令这种机制创建出自己的子类实例。Object-C中有一项强大的特性可以解决这个问题,这就是“关联对象”(Associated Object)

    关联类型 等效的@property
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
    OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
    OBJC_ASSOCIATION_RETAIN retain
    OBJC_ASSOCIATION_COPY copy

    下列方法可以管理关联对象:

    void objc_setAssociatedObject (id object,void *key,id value,objc_AssociationPolicy policy)
    此方法以给定的键和策略为某对象设置关联对象值。
    
    id objc_getAssociatedObjects(id object,void *key)
    此方法根据给定的键从某对象中获取相应的关联对象值。
    
    void objc_removeAssociatedObjects(id object)
    此方法移除指定对象的全部关联对象。
    

    关联对象用法举例:
    ios 开发时经常用到UIAlertView类,该类提供了一种标准试图,用来提示信息。但是创建的代码和处理按钮动作的代码是分开的,如:

    - (void)viewDidLoad {
    UIAlertView *view = [[UIAlertView alloc] initWithTitle:@"标题" message:@"消息" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"1", nil];
        [view show];
    }
    
    #pragma mark --UIAlertViewDelegate
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
         if (buttonIndex == 0) {
                NSLog(@"0");
            }else{
                NSLog(@"1");
            }
    }
    

    如果想在同一个类里处理多个警告信息视图,那么代码就会变得更加复杂,我们必须在delegate方法中检查传入的alertview参数,并据此选用相应的逻辑。要是能在创建视图的时候就直接把处理每个按钮的逻辑都写好,就简单多了。这可以通过关联对象来做。创建完视图后,设定一个与之关联的“块(block)”等到执行delegate方法时再将其读出来,如下:

    #import <objc/runtime.h>
    
    static void *key = @"key";
    - (void)viewDidLoad {
    UIAlertView *view = [[UIAlertView alloc] initWithTitle:@"标题" message:@"消息" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"1",@"2", nil];
    
    void (^block)(NSInteger) = ^(NSInteger buttonIndex){
            if (buttonIndex == 0) {
                NSLog(@"0");
            }else{
                NSLog(@"1");
            }
     };
        
     objc_setAssociatedObject(view, key, block, OBJC_ASSOCIATION_COPY);
     [view show];
    }
    
    #pragma mark --UIAlertViewDelegate
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        void (^block)(NSInteger) = objc_getAssociatedObject(alertView, key);
        block(buttonIndex);
    }
    
    

    以这种写法,创建和处理的代码放在一起,比较方便查阅。但是,采用该方法时需要注意:块可能要捕获某些变量,也许会造成“循环引用”。

    • 可以通过“关联对象”机制来把两个对象连起来。
    • 定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非用有关系”。
    • 只有在其他方法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug。

    11、理解objc_msgSend的作用

    id returmValue = [someObject messageName:parametter];
    someObject叫做“接收者”,messageName叫做“选择子(selector)”。选择子和参数合起来称为“消息(message)”。编译器看到后将其转换成标准的C语言函数调用,所调的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,其“原型”如下:
    
    void objc_msgSend(id self,SEL cmd,……)
    第一个参数是接收者,第二个参数是选择子,后续参数就是消息中那些参数,其顺序不变。
    
    

    在实际开发中,大家无须担心这一问题,不过应该了解其底层工作原理。

    12、理解消息转发机制

    • 若对象无法响应某个选择子,则进入消息转发流程
    • 通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中。
    • 对象可以把其无法解读的某些选择子转交给其他对象来处理
    • 经过上述两步之后,如果还是没办法处理选择子,那就启动完整的消息转发机制。

    相关文章

      网友评论

          本文标题:编写高质量iOS的52个有效方法学习笔记(6-12)

          本文链接:https://www.haomeiwen.com/subject/zuvscxtx.html