zoukankan      html  css  js  c++  java
  • objective-c 语法快速过(3)

    oc 里的匿名对象 

     oc 这里,很少用到,因为并不适用于oc的内存管理,只是面试笔试也许出现,要求能看懂,不要在项目里这样写,因为写匿名对象,会造成内存泄露

    #import <Foundation/Foundation.h>
    @interface Car : NSObject
    {
        @public
        int speed;
    }
    - (void)run;
    @end
    
    @implementation Car
    - (void)run
    {
        NSLog(@"%d", speed);
    }
    @end
    int main()
    {
        //所谓匿名对象,就是没有名字的对象,看不到,但是对象确实存在
        [Car new]->speed = 300;//没有指针变量指向对象,而是直接调用类的成员变量,因为每次使用[Car new]都会从新创建一个新对象,故不是300,而是默认初始化的值0
        [[Car new] run];//0
        
       // Car *c = [Car new];
       // c->speed = 100;
       // [c run];//100
        return 0;
    }

    能看懂什么意思就行

    类的成员变量的命名规范

    • 成员变量都以下划线 _ 开头
    • 可以跟get方法的名称区分开
    • 可以跟其他局部变量区分开,一看到下划线开头的变量,肯定是类的成员变量

    OC弱语法

    1、OC是动态检测错误,OC里调用一个没有声明也没有实现的对象方法,则不会编译报错而是警告,链接也能通过,只有运行才检测出错

    2、Oc里调用只有声明,但是没有实现的对象方法,这编译也是警告,链接通过,运行才出错

    3、Oc调用只有实现(没声明)的方法,则没有问题!因为运行时才检测程序的问题,声明其实只是摆设,删掉是没事的。只不过开发中,必须规范!该写都要写上。即使不报错。

    类方法

    直接可以用类名来执行的方法(类本身会在内存中占据存储空间,里面有类对象方法列表)

    1.  类方法和对象方法对比

    1)   对象方法

    • 减号-开头
    • 只能让对象调用,没有对象,这个方法根本不可能被执行
    • 对象方法能访问实例变量(成员变量)

    2)   类方法

    • 加号+开头
    • 只能用类名调用,对象不能调用
    • 类方法中不能访问实例变量(成员变量)
    • 使用场合:当不需要访问成员变量的时候,尽量用类方法

    3)       类方法和对象方法可以同名

    4)       提高程序性能

    类方法的好处和使用场合:不依赖于对象,执行效率高, 能用类方法,尽量用类方法

    // 类方法都是以+开头
    + (void)printClassName;
    - (void)test;
    + (void)test; //可以允许类方法和对象方法同名

    self 关键字用来指明对象是当前方法的接收者。 

    • 当成员变量和局部变量同名时,采取就近原则,访问的是局部变量
    • 用self访问成员变量,区分同名的局部变量

    细节:

    1)    出现的地方:可以出现在所有的OC方法中(对象方法类方法),但是不能出现在函数里

    2)    作用:

    使用 "self->成员变量名" 访问当前方法调用的成员变量

    使用 "[self 方法名];" 来调用方法(对象方法类方法)

    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    {
        int _age;
    }
    - (void)setAge:(int)age;
    - (int)age;
    - (void)test;
    @end
    
    @implementation Person
    - (void)setAge:(int)age
    {
        // _age = age;相当于
        self->_age = age;
    }
    - (int)age
    {
        return self->_age;
    }
    - (void)test
    {
        // self:指向了方向调用者,代表着当前对象
        int _age = 20;
        NSLog(@"Person的年龄是%d岁", self->_age);
    }
    
    @end
    
    int main()
    {
        Person *p = [Person new];
        
        [p setAge:10];
        
        [p test];
        
        return 0;
    }
    /* self的用途:
     1> 谁调用了当前方法,self就代表谁
     * self出现在对象方法中,self就代表对象
     * self出现在类方法中,self就代表类
     2> 在对象方法利用"self->成员变量名"访问当前对象内部的成员变
    [self 方法名]可以调用其他对象方法类方法 */
    #import <Foundation/Foundation.h>
    @interface Dog : NSObject
    - (void)bark;
    - (void)run;
    @end
    
    @implementation Dog
    - (void)bark
    {
        NSLog(@"汪汪汪");
    }
    - (void)run
    {
        [self bark];//self代表指针d指向的对象,NSLog(@"汪汪汪");
        NSLog(@"跑跑跑");
    }
    @end
    
    int main()
    {
        Dog *d = [Dog new];
        
        [d run];
        
        return 0;
    }

    低级错误:

    用self去调用函数

    类方法中用self调用对象方法,对象方法中用self调用类方法

    self死循环

    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    - (void)test;
    + (void)test;
    
    - (void)test1;
    + (void)test2;
    
    - (void)haha1;
    + (void)haha2;
    @end
    
    @implementation Person
    - (void)test
    {
        NSLog(@"调用了-test方法");
        // 如果有[self text];这句,就会引发死循环。因为[self test];self代表对象,一直调用对象方法test
    }
    + (void)test
    {
        NSLog(@"调用了+test方法");
        // 引发死循环 [self test];self代表类,一直调用类方法test
    }
    //自动识别
    - (void)test1
    {
        [self test]; // ok,-test
    }
    
    + (void)test2
    {
        [self test]; // ok,+test
    }
    
    - (void)haha1
    {
        NSLog(@"haha1-----");
    }
    //函数
    void haha3()
    {  
    }
    
    + (void)haha2
    {
         haha3();//ok,一定注意函数和方法是不一样的!
        //[self haha3];//error,不能用self调用函数
        //[self haha1];//error,类方法里,不能用self调用对象方法,相反,在对象方法里,也不能用self调用类方法
    }
    @end
    
    int main()
    {
        [Person haha2];//直接调用类方法
        //Person *p = [Person new];
        //[p test1];
        return 0;
    }

    原则上(如果不使用 ARC,也就是自动引用计数),那么

    创建一个新对象,都要请求分配内存,在完成对该对象的操作时,必须释放其所用的内存空间。类似 c++的内存管理。

    与 C 语言兼容的地方:

    预处理:

    #define 语句和 c 一样

    #运算符: #define str(x) #x

    表示在调用该宏时,预处理程序根据宏参数创建C 风格的常量字符串。

    例如:str("hello")将产生""hello""

    ##运算符: 表示用于把两个标记连在一起

    #import 语句;相当于#include 语句,但是 #import 可自动防止同一个文件被导入多次。

    #条件编译语句(#ifdef 、#endif 、 #else 、 #ifndef)和 C 一样

    #undef 语句 消除特定名称的定义


    其他基本的C 语言特性:

    数组、函数、指针、结构、联合的用法和C 一样。

    Compound Literal 是包含在括号之内的类型名称,之后是一个初始化列表。

    例如

    如果 intPtr 为 int * 类型:
    intPtr = (int[100]){[0] = 1, [50] = 50, [99] = 99};

    如果数组大小没有说明,则有初始化列表确定。

    其他如循环语句 (do while、while、for) 、条件语句(if 语句(if-else、复合判断条件等) 、switch 语句)、 Boolean(YES NO)、条件运算符、goto 语句、空语句、逗号表达式、sizeof 运算符、命令行参数、位操作都 和 C 一样 。

    oc 的继承

    oc的继承只支持单一继承,和java类似,也就是儿子只能有一个爸爸,但是

    可以通过 Objective-C 的分类和协议特性获取多继承的优点 

    而c++支持单一和多重继承。

    好处:

    不改变原来模型的基础上,拓充方法

    建立了类与类之间的联系

    抽取了公共代码

    坏处:

    耦合性强

    基本上所有类的根类是NSObject类

    如图:

    animal类拥有NSObject类的new方法,子类dog和cat同时拥有前两层类的所有方法和属性。类似 c++和 java

    #import <Foundation/Foundation.h>
    /* 1.继承的好处:
     1> 抽取重复代码
     2> 建立了类之间的关系
     3> 子类可以拥有父类中的所有成员变量和方法
     2.注意点
     1> 基本上所有类的根类是NSObject
     */
    /********Animal的声明*******/
    @interface Animal : NSObject
    {
        int _age;
        double _weight;
    }
    
    - (void)setAge:(int)age;
    - (int)age;
    
    - (void)setWeight:(double)weight;
    - (double)weight;
    @end
    /********Animal的实现*******/
    @implementation Animal
    - (void)setAge:(int)age
    {
        _age = age;
    }
    - (int)age
    {
        return _age;
    }
    
    - (void)setWeight:(double)weight
    {
        _weight = weight;
    }
    - (double)weight
    {
        return _weight;
    }
    @end
    
    /********Dog*******/
    // 继承了Animal,相当于拥有了Animal里面的所有成员变量和方法
    // Animal称为Dog的父类,Dog称为Animal的子类
    @interface Dog : Animal
    @end
    
    @implementation Dog
    @end
    
    /********Cat*******/
    @interface Cat : Animal
    @end
    
    @implementation Cat
    @end
    
    int main()
    {
        Dog *d = [Dog new];
        
        [d setAge:10];
        
        NSLog(@"age=%d", [d age]);
        return 0;
    }

    oc 继承里的细节(类似其他面向对象语言)

    父类必须声明在子类的前面

    子类和父类不能有相同的成员变量,但是方法可以重写

    方法的重写问题:子类重新实现父类中的某个方法,也就是覆盖了父法

    调用某个方法时,优先去当前类中找,如果找不到,去父类中找

    这是内部原理。

    每个对象都有一个isa指针,指向对象属于的类,且记住:每个类里(oc的)都有一个superclass指针,指向自己的父类。

    这样通过对象就能找到对象属于的类,也能找到类的父类。而这些指针就在NSSobject类里。

    内存结构:

    继承的使用场合

     1> 当两个类拥有相同属性和方法的时候,就可以将相同的东西抽取到一个父类中

     2> 当A类完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类

     // 继承:xx 是 xxx

     // 组合(也叫聚合关系):xxx 拥有 xxx

    super关键字

    实现重写之后,还可以调用父类的对象方法和类方法,super的作用;直接调用父类中的某个方法

     1、super处在对象方法中,那么就会调用父类的对象方法

     2、super处在类方法中,那么就会调用父类的类方法

    使用场合:

    子类重写父类的方法时想保留父类的一些行为。

    在oc里,简单的多,调用某方法,先是在所在类就近找,找不到去父类找。太简单了。如果重写了父类方法,那么只能调用子类重写的这个方法了,如果想保留父类原方法定义的功能,可以用super。

    oc 的多态

    多态的体现

    Person *p = [Student new];

    p->age = 100;

    [p walk];

    子类对象赋值给父类指针,父类指针访问对应的子类的继承来的属性和方法

    多态的局限性

    不能访问子类的特有的属性或方法(可以考虑强制转换)

    多态的细节

    动态绑定:在运行时根据对象的类型确定动态调用的方法

    注意点:

     1.没有继承就没有多态

     2.代码的体现:父类类型的指针指向子类对象

     3.好处:如果函数方法参数中使用的是父类类型,可以传入父类、或者子类对象

     4.局限性:父类类型的变量 不能 直接调用子类特有的方法。必须强转为子类类型变量后,才能直接调用子类特有的方法(类似c++的赋值兼容)

    #import <Foundation/Foundation.h>
    // 动物
    @interface Animal : NSObject
    - (void)eat;
    @end
    
    @implementation Animal
    - (void)eat
    {
        NSLog(@"Animal-吃东西----");
    }
    @end
    //
    @interface Dog : Animal
    - (void)run;//子类新增的对象方法run
    @end
    
    @implementation  Dog
    - (void)run
    {
        NSLog(@"Dog---跑起来");
    }
    - (void)eat//重写父类的-eat方法
    {
        NSLog(@"Dog-吃东西----");
    }
    @end
    
    //
    @interface Cat : Animal
    @end
    
    @implementation Cat
    - (void)eat//重写父类的对象方法eat
    {
        NSLog(@"Cat-吃东西----");
    }
    @end
    // 如果参数中使用的是父类类型指针,可以传入父类or子类对象
    void feed(Animal *a)
    {
        [a eat];
    }
    
    int main()
    {
        Animal *aa = [Dog new];//父类指针指向子类的对象
        //[aa run];弱语法,只警告!但是在java或者c++里,早就报错了!父类指针不能访问子类特有的方法,虽然弱语法,但不推荐,自己要认为是错的
        // 将父类对象aa转为子类 Dog * 类型的变量就ok了,和c++类似
        Dog *dd = (Dog *)aa;
        [dd run];//ok,访问的是子类的对象方法 run 
        
        //Dog *d = [Dog new];
        //[d run];//ok
        
        /* Animal *aa = [Animal new];父类对象的指针aa
        feed(aa);可以传入父类对象做参数,调用的父类的eat方法
        Dog *dd = [Dog new];子类对象指针dd
        feed(dd); 也可以传入子类的对象做参数,调用的子类的eat方法
        Cat *cc = [Cat new];
        feed(cc);调用子类eat,传入子类对象参数   */
    
        // 多态:父类指针指向子类对象
        Animal *a = [Dog new];
        // 调用方法时会检测对象的真实形象,动态
        [a eat];调用的时子类的eat方法
        */
        return 0;
    }

    多态的局限性:

    父类类型的变量 不能 直接 调用子类特有的方法。

    联系c++

    c++是使用了虚函数,(包括纯虚函数和抽象类)对公有继承的子类的方法,重写虚函数,父类指针或者引用指向子类,那么就能调用子类的重写虚函数,指向父类,就是调用父类的虚函数,实现动态联编。

    oc里,没有那么复杂,就是直接子类继承父类,那么重写父类某个对象方法,这只需要父类指针指向子类对象,那么就调用子类的重写方法,同样也不能调用子类特有的方法。

    由此断定,oc的方法都是虚方法!不用和c++一样用virtual声明!且oc的重写也是多态的一种,oc里所有的方法访问属性都是公有的!而类成员变量默认是保护的。和c++有些区别,c++是默认都是私有的。

    再看oc的弱语法!比如把子类对象指针指向父类

    Cat *cat = [Animal new];

    c++里肯定错误,但oc里没问题,只警告,但这样不好,不推荐。完全没道理。

    再看:

    NSString *s = [Cat new];

    动物怎么了成了字符串对象了?oc里xcode不报错!但是绝对是不对,不规范。还有,同一个层次的类,猫类指针指向狗类对象,调用eat方法,在oc的弱语法下,还是没问题的,调狗的eat。

    oc太弱了!但是在c++里直接就报错了。不可以这样写,即使编译器不报错。

    多态的好处

    用父类对象接收参数,方法或者函数,即可以接受子类对象,也能接受父类对象,节省代码。否则还要分开写多个方法或者函数。

    充分体现面向对象的抽象和具体,通过子类重写继承来的父类的虚方法(oc默认都是),通过父类指针指向子类对象,实现动态多态性!不看指针的类型,而是看指针指向的哪个对象类型,就调用哪个子类对象的虚方法。注意子类特有的方法不可以,必须强转。

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效(二)
    C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效(一)
    NPOI Excel 单元格背景颜色对照表
    Go语言中的字符和字符串
    ueditor上传图片跨域问题解决
    OAuth在WebApi中的使用,前后台分离的调用方式
    Spark集群高可用HA配置
    安装配置Spark集群
    .net学习
    修改MvcPager分页控件以适用Bootstrap 效果(含英文版,可下载)
  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/4311257.html
Copyright © 2011-2022 走看看