黑马程序员-8-Objective-C学习笔记(OC特有语法)
黑马程序员-8-Objective-C学习笔记(OC特有语法)
明天过后1 发表于3年前
黑马程序员-8-Objective-C学习笔记(OC特有语法)
  • 发表于 3年前
  • 阅读 53
  • 收藏 0
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

摘要: Category, NSLog输出补充, SEL, @property,block,@protocol

---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ---------------------- 

1. Category

    a. 简介

        (1) 在不改变原来类模型的前提下,给类扩充一些方法 : a. 继承           b. Category

        (2) 继承 和 Category 区别 : 

            1) 使用思想不同,继承是子类在父类的基础之上拓展功能,子类与父类的逻辑关系明显,Category多用于团队分模块开发,在现有基础之上增加新功能。

            2) Category 不能修改成员变量的数目,而继承中子类可以增加成员变量的数目

            3) Category重写原生方法后可能会导致原生方法无法访问,而继承重写父类方法仍然可以访问父类的方法。

    b. 简单使用            

    // Person.h
    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    - (void) test();
    @end
    
    // Person.m
    #import "Person.h"
    @implementation Person
    - (void) test
    {
        NSLog(@"test...");
    }
    @end  
    
    // Person+Person1.h
    #import "Person.h"
    @interface Person (Person1)
    - (void) test1();
    @end
    
    // Person+Person1.m
    #import "Person+Person1.h"
    @implementation Person (Person1)
    - (void) test1
    {
        NSLog(@"test1...");
    }
    @end  
    
    // main.m
    #import "Person.h"
    #import "Person+Person1.h"
    
    int main(){
        Person *p = [[Person alloc] init];
        // 优先去分类中查找,然后再去原来类中找,最后再去父类中找
        [p test]; // test...
        [p test1]; // test1...
        return 0;
    }

    c. 总结

                (1) Category 好处是一个庞大的类可以分模块来开发,更利于团队开发

                (2) 类对象调用方法的时候优先去分类中查找,然后再去原来类中找,最后再去父类中找

                (3) 不建议覆盖原有的方法,因为会导致源有方法无法使用。

                (4) 覆盖后该使用哪个方法决定于编译的优先级,查看编译的优先级:build phases->compile sources查看优先级,分类之间同名方法的优先级取决与分类的.m文件编译的顺序,比较靠后的优先级比较高


2. NSLog输出补充

    a. 预处理宏符号

关键字 NSLog中的占位符 描述
__func__ %s 当前方法签名
__LINE__ %d 此代码在源文件中的第几行
__FILE %s 该源文件的全路径
__PRETTY_FUNCTION__
%s 功能跟 __func__ 一样,不过此符号用于C++源文件中

    b. 如何获得常用的输出字符串

表达式 占位符 描述
NSStringFromSelector(__cmd) %@ 当前方法签名字符串
NSStringFromClass([self class]) %@ 当前类名字符串
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] %@ 当前源文件的名字字符串
[NSThread callStackSymbols] %@ 获得程序运行时栈的可读设置数组,只适用于调试


3. SEL

    a. SEL类型的定义

            OC提供了一种新类型数据对类方法进行操作,源码中的原生定义如下:

                typedef struct objc_selector *SEL;

    a. 类方法存储位置

             对象调用方法都是通过isa指针去类对象中寻找,因此对象方法存储的位置是类对象中 : 

                 (1) 每个类的方法列表都存储在类对象中

                 (2) 每个方法都有一个与之对应的SEL类型的对象

                 (3) 根据一个SEL对象就可以找到方法的地址,进而调用方法

    c. SEL对象的创建

    SEL s = @selector(test);
    SEL s2 = NSSelectorFromString(@"test");


4. @property

        @property是编译器特性,它可以帮助我们自动生成一些代码,其中包括可以自动生成成员变量,自动生成成员变量的setter 和 getter.

    a. @property会生成什么代码   

        (1) @property 如果不加任何参数会根据情况生成 属性 和 普通的 setter,getter

            1) 如果已经定义了要生成的属性(默认私有),那么编译器不会去定义

            2) 如果已经声明了或者定义了要生成 setter 和 getter 将不会去 声明 和 定义

            3) 生成的代码取决于 @property 后面的变量名

    #import <Foundation/Foundation.h>

    @interface Cat : NSObject
    // @property自动生成如下代码
    /*
    {
        @private
        int _age;
    }
    -(void) setAge:(int)age;
    - (int) age;
    */
    // 注意 : 生成的属性名称是在前面加上一条下划线 : _age
    @property int age;
    @end
    
    @implementation Cat
    // @property自动生成如下代码
    /* 普通的setter 和 getter
    -(void) setAge:(int)age
    {
        _age = age;
    }
    - (int) age
    {
        return _age;
    }
    */
    @end

    b. @property 的参数以及作用

            (1) set方法内存管理相关参数:

                 retain: release旧值,retain新值

                 assign: 直接赋值 (缺损值)

                 copy: 释放旧值,copy新值,此属性只对那些实行了NSCopying协议的对象类型有效

    // retain
    @property (retain) NSString *name;
    /* 生成代码
    - (void) setName:(NSString *)name
    {
        if ( _name != name ){
            [_name release];
            _name = [name retain];
        }
    }
    */
    
    // assign
    @property (assign) NSString *name;
    /* 生成代码
    - (void) setName:(NSString *)name
    {
        _name = name;
    }
    */
    
    // copy
    @property (copy) NSString *name;
    /* 生成代码
    - (void) setName:(NSString *)name
    {
        if ( _name != name ){
            [_name release];
            _name = [name copy];
        }
    }
    */


             (2) set方法是否生成:

                 readonly: 只生成get方法。

                 readwrite: set方法和get方法都生成(缺损值)

             (3) 多线程管理:

                 noatomic: 性能高,但线程不安全

                 atomic: 性能低,但线程安全

             (4) set方法和get方法的方法名字:

                 setter : 决定了set方法的名称,一定要有个冒号 :

                 getter : 决定了get方法的名称(一般用在BOOL类型)

        // setter
        @property (setter = setNewAge:) int age;
        /* 生成代码
        - (void) setNewAge:(int)age
        {
            _age = age;
        }
        */
        
        // getter 
        @property (getter = isOk:) BOOL ok;
        /* 生成代码
         - (BOOL) isOk
         {
             return _ok;
         }
        */

    c. 使用注意

        (1) 通常情况下为了性能高一般会加上 noatomic 参数

        (2) 对于基本数据类型使用 assign OC对象类型使用 retain

        (3) 为了提高可读性建议加上 readwrite 或者 readonly 参数

    // 基本数据类型
    @property (noatomic,readwrite,assign) int age;
    
    // OC对象类型
    @property (noatomic,readwrite,retain) NSString *name;


5. block

    a. 简介

            代码块本质上和变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。

             block的声明与函数指针声明很像,不过block有它特有的标志(^),如下图 :

                    

    b. 简单使用

    #import <Foundation/Foundation.h>
    
    int main(){
        void (^print)(NSString *str);
        print = ^(NSString *str){
            NSLog(str);
        };
        print(@"hello world");
        return 0;
    }

    c. block的递归调用

    #import <Foundation/Foundation.h>
    // 注意递归调用block前要把block的函数体定义出来
    int main(){
        int (^accumulate)(int) = ^(int a){
            int sum = a;
            if ( a > 0 ){
                a--;
                sum += accumulate( a-- );
            } else {
                return sum;
            }
        };
        NSLog(@"accumulate 5 = %d",accumulate(5));
        return 0;
    }

    d. block访问外部变量

    #import <Foundation/Foundation.h>
    /*
        block代码块访问block之外的变量如果不加任何处理编译会报错
        因此如果block要访问外部变量,请给变量加上__block关键字
    */
    // 这里结合typedef使用block
    typedef int (^MyBlock)(int);
    int main(){
        __block int a = 10;
        MyBlock addWithA;            // block的声明
        addWithA= ^(int b){
            b += a;
            return b;
        };
        int result = addWithA(10);
        NSLog(@"a + b = %d",result);
        return 0;
    }


6. @protocol

    a. 简介

            (1) @protocol,简称协议,主要是用来方便程序员之间交流的一种OC特性,它与java中的接口相似,它的组成是 @protocol + 方法列表 + @end;

           (2) 它的结构和使用模板如下 : 

         1.协议的定义        
             @protocol 协议名称 <NSObject>
              @required
              // 方法声明列表....
              @optiuonal
              // 方法声明列表
             @end
         
         
         2.如何遵守协议
             1> 类遵守协议
             @interface 类名 : 父类名 <协议名称1, 协议名称2>
             @end
         
             2> 协议遵守协议
             @protocol 协议名称 <其他协议名称1, 其他协议名称2>
             @end

            (3) 协议提供两个关键字用来限定方法是否强制实现 : 

                1) @required (默认),强制实现,如果不实现会报警告.由于OC是弱语法,因此如果类没有实现required方法编译也不会报错

                2) @optional , 可以不实现,如果不实现也不会包警告

    b. 使用

            (1) 对于协议的定可以放在其他文件中 也可以单独新建文件存放,一般建议会单独新建文件存放

            (2) 如果要求类要遵守某个协议,那么必须让这个协议遵守<NSObject>协议,否则该类的实例会失去NSOject的某些方法。

            (3) 协议也可以对类的成员变量进行限定

    // 以下代码暂时不考虑内存管理
    /*------------------------------------------- ClassProtocol.h -------------------------------------------*/
    #import <Foundation/Foundation.h>
    @protocol ClassProtocol <NSObject>
    
    @required
    - (void) test; 
    
    @optional
    - (void) test1;
    
    @end
    
    /************************************************************************************************************/
    
    /*------------------------------------------- PropertyProtocol.h -------------------------------------------*/
    #import <Foundation/Foundation.h>
    @protocol ClassProtocol <PropertyProtocol>
    
    - (void) test2;
    
    @end
    /************************************************************************************************************/
    
    /*------------------------------------------- Dog.h -------------------------------------------*/
    #import <Foundation/Foundation.h>
    @protocol PropertyProtocol <NSObject>
    
    @interface Dog: NSObject <PropertyProtocol>
    
    @end
    /************************************************************************************************************/
    
    /*------------------------------------------- Dog.m -------------------------------------------*/
    #import "Dog.h"
    #import "PropertyProtocol.h"
    
    @implementation Dog
    
    - (void) test2
    {
        NSLog(@"PropertyProtocol  default required -- test2");                        // 实现 PropertyProtocol的 默认 required 方法    
    }
    
    @end
    /************************************************************************************************************/
    
    /*------------------------------------------- Person.h -------------------------------------------*/
    #import <Foundation/Foundation.h>
    #import "Dog.h"
    
    @protocol ClassProtocol                                    // 告诉编译器,Test是一个协议,在使用该协议具体定义的函数时候才把该协议文件import进来,提高编译效率
    @protocol PropertyProtocol
    
    @interface Person : NSObject <ClassProtocol>
    
    @property (nonatomic,retain) Dog<PropertyProtocol> *dog; // 协议对成员变量限定   
    // @property (nonatomic,retain) id<PropertyProtocol> *dog;  //等价于上一句,不过可能会失去Dog特有的方法
    
    @end
    /************************************************************************************************************/
    
    /*------------------------------------------- Person.m -------------------------------------------*/
    
    #import "Person.h"
    #import "ClassProtocol.h"
    
    @implementation Person
    
    - (void) test
    {
        NSLog(@"ClassProtocol required -- test");                        // 实现 ClassProtocol 的 required 方法    
    }
    
    
    - (void) test1
    {
        NSLog(@"ClassProtocol optional -- test1");                        // 实现 ClassProtocol 的 optional 方法    
    }
    
    @end
    /************************************************************************************************************/
    
    
    /*------------------------------------------- main.m -------------------------------------------*/
    
    #import "Person.h"
    #import "Dog.h"
    
    #import "ClassProtocol.h"
    #import "PropertyProtocol.h"
    
    int main{
        Person<ClassProtocol> *p = [[Person alloc] init];                  // 显式实例化一个遵守 ClassProtocol 的Person对象
        // id<ClassProtocol> *p = [[Person alloc] init];                   // 显式实例化一个遵守 ClassProtocol 的 OC 对象,该对象只保留了 ClassProtocol 里面的方法,失去了Person特有的方法
        Dog *dog = [[Dog alloc] init];                                     // 实例化一个遵守 PropertyProtocol 的Dog对象
        [dog test2];                                                       // 调用实现协议的方法
        
        [p setDog:dog];                                                    // 对协议限定的成员变量赋值,如果dog变量不遵守 PropertyProtocol 编译会报错
        
        [p test];
        [p test1];
        return 0;
    }
    
    /************************************************************************************************************/



---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ---------------------- 

 详情请查看:http://edu.csdn.net/heima



  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 2
博文 20
码字总数 36166
×
明天过后1
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: