美文网首页
[GeekBand]Objective-C编程语言第一周笔记

[GeekBand]Objective-C编程语言第一周笔记

作者: Hran233 | 来源:发表于2016-02-27 12:13 被阅读0次

    1. Objective-C简介

    Objective-C语言简介

    • Objective-C在C语言基础上做了面向对象扩展。
    • 1983年由Brad Cox和Tom Love发明,后成为NeXT的主力语言,后被苹果收购,成为苹果开发平台的主力语言。
    • 与Cocoa和Cocoa Touch框架高度集成,支持开发Mac OS X、iOS应用。
    • 在苹果开发平台上,通过LLVM编译器架构,支持与Swift语言双向互操作。
      在iOS开发平台上支持的语言有Swift、Objective-C、C/C++,主要使用前两者。

    如何掌握高级编程语言

    底层思维:向下,如何把握机器底层从微观理解对象构造

    • 语言转换
    • 编译转换
    • 内存模型
    • 运行时机制

    抽象思维:向上,如何将我们的周围世界抽象为程序代码

    • 面向对象
    • 组件封装
    • 设计模式
    • 架构模式

    「时空人」三位一体分析法

    • 对时间分析——发生在什么时候?
      • Compile-time VS Run-time
    • 对空间分析——变量放在哪里?
      • Stack VS Heap
    • 人物分析——代码哪里来的?
      • Programmer VS Compiler/Runtime/Framework

    两种开发方式

    • Clang或GCC命令行
      • clang -fobjc-arc HelloWorld.m -o HelloWorld
      • gcc -fobjc-arc HelloWorld.m -o HelloWorld,推荐用clang
      • -fobjc-arc:支持ARC(Automatic Reference Counting)
      • 适合调试、研究、微观探查
    • -o:输出文件名
    • 更多可以参考-help
    //引入头文件,可以避免多次引入重复头文件,推荐使用。
    #import <Foundation/Foundation.h>
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            //类似于prinft,但多了日期时间等信息。
            NSLog(@"Hello World~");
        }
        return 0;
    }
    
    • Objective-C的字符串前要加@
    • Xcode项目
      • 构建正规工程项目
      • 使用大型框架,追求设计质量与代码组织

    Objective-C编译过程

    • GCC: GCC Front End -> GCC Optimizer -> GCC Code Generator
    • LLVM-GCC: GCC Front End -> LLVM Optimizer -> LLVM Code Generator
    • LLVM-Clang: Clang Front End -> LLVM Optimizer -> LLVM Code Generator
    • Objective-C直接生成机器码
    • LLVM:Low Level Virtual Machine
    • LLVM-Clang:目前iOS上一般使用的方式,针对iOS优化更好。

    学习资源

    课程总结

    • 了解Objective-C语言和编译架构
    • 了解Objective-C开发工具
    • 了解机器思维和抽象思维
    • 驾驭工具,而不是被工具驾驭
    • 掌握英文学习资源很重要

    2. 类与对象

    类型系统

    • 引用类型 Reference Type
      • 类 Class
      • 指针 Pointer
      • 块 Block
    • 值类型 Value Type
      • 基础数值类型
      • 结构 Struct
      • 枚举 Enum
    • 类型装饰
      • 协议 Protocol
      • 类别 Category
      • 扩展 Extension
    • C语言中的数据类型均可用在Objective-C中

    类 VS 结构

    • 类型与实例
      • 类与对象
      • 结构与值
    • 类——引用类型
      • 位于栈上的指针(引用)
      • 位于堆上的实体对象
    • 结构——值类型
      • 实例直接位于栈中
    • 空间分析
      • 运行时内存图——「胸中有沟壑」

    类定义

    RPoint.h

    @interface RPoint: NSObject
    
    @property int x; //属性,状态,这里默认初始化为0
    @property int y; 
    
    - (void) print; //方法,行为
    
    @end
    
    • - (void)print-表示实例方法,+ 表示类方法,其后的括号内为返回值类型。

    RPoint.m

    #import <Foundation/Foundation.h>
    #import "RPoint.h"
    
    @implementation RPoint
    
    - (void) print {
        NSLog(@"[%d, %d]", self.x, self.y);
    }
    
    @end
    

    SPoint.h

    typedef struct {
        int x;
        int y;
    }SPoint;
    
    对象的空间分析

    栈上存储指针(引用),堆上存储真正的对象,

    值的空间分析

    实例(值)内存直接存储在栈空间

    栈 VS 堆

    栈:存储值类型

    • 无ARC负担,由系统自动管理,以执行函数为单位
    • 空间大小编译时确定(参数+局部变量)
    • 函数执行时,系统自动分配一个Stack
    • 函数执行结束,系统立即自动回收Stack
    • 函数之间通过拷贝值传递
    • 具有局部性,大小有限额,超出会Stack Overflow

    堆:存储引用类型对象

    • 分配由程序员手动请求(创建对象时)
    • 释放由运行时ARC机制自动释放(确定时)
    • 函数之间通过拷贝引用(指针)传递
    • 具有全局性,总体无大小限制(受制于系统内存整体大小)

    main.m

    #import <Foundation/Foundation.h>
    #import "RPoint.h"
    #import "SPoint.h"
    
    void process(RPoint* rp3, SPoint sp3);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            //Objective-C中所有的对象均以指针的形式存在,要加 * 号
            //中括号为发送消息(方法调用)
            //创建对象,alloc为向系统请求内存分配
            RPoint* rp1 = [[RPoint alloc] init];
            
            rp1.x = 10;
            rp1.y = 20;
    
            [rp1 print]; //[10, 20]
    
            //
            SPoint sp1;
            sp1.x = 10;
            sp2.y = 20;
    
            NSLog(@"------拷贝------")
            RPoint rp2 = rp1;
            rp2.x++;
            rp2.y++;
    
            //引用传递
            [rp1 print]; //[11, 21]
            [rp2 print]; //[11, 21]
    
            SPoint sp2 = sp1;
            sp2.x++;
            sp2.y++;
    
            //值传递
            NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
            NSLog(@"[%d, %d]", sp2.x, sp2.y); //[11, 21]
    
            NSLog(@"------传参------");
            process(rp1, sp1);
            [rp1 print]; //[12, 22]
            NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
        }
        return 0;
    }
    //函数开始执行时自动创建一个新栈,与main函数的栈不同,结束时会自动销毁
    void process(RPoint* rp3, SPoint sp3) {
        rp3.x++;
        rp3.y++;
        
        sp3.x++;
        sp3.y++;
    
        [rp3 print]; //[12, 22]
        NSLog(@"[%d, %d]", sp3.x, sp3.y); //[11, 21]
        //函数执行结束后rp3和sp3将被销毁
        //但rp3仅是指针,销毁后指针所指向的对象并不受影响。
    }
    

    3. 数据成员:属性与实例变量

    类型成员 Type Member

    数据成员 Data Member 描述对象状态

    • 实例变量 Instance Variable
    • 属性 Property

    函数成员 Function Member 描述对象行为

    • 方法 Method
    • 初始化器 Init
    • 析构器 Dealloc

    认识属性

    属性表达实例状态,描述类型对外接口。相比直接访问实例变量,属性可以做更多控制。

    默认情况下,编译器会为属性定义propertyName自动合成:

    • 一个getter访问器方法:propertyName
    • 一个setter访问器方法:setPropertyName
    • 一个实例变量:_propertyName

    可自定义访问器方法,也可更改访问器方法名、或实例变量名。

    可以使用静态全局变量(C语言)+ 类方法,模拟类型属性。

    属性声明:

    @property NSString* firstName;
    //--上述代码将生成:--
    NSString* _firstName;
    - (NSString*) firstName {/**code**/}
    - (void) setFirstName: (NSString*)newValue {/**code**/}
    //----
    
    //更改访问器方法名
    @property (getter = GivenName, setter = setGivenName:) NSString* lastName;
    //更改实例变量名
    @synthesize firstName = givenName;
    

    调用方法:

    //访问器方法
    [employee setFirstName: @"Tom"];//set
    [employee firstName]; //get
    
    //属性表达式,推荐。
    employee.firstName = @"Tom"; //set
    NSLog(@"First Name: %@", employee.firstName);//get
    
    模拟静态(类)属性

    在.m文件中定义一个静态变量

    //静态变量
    static int _max = 100;
    
    @implementation Employee {
        //...
    }
    

    在.h文件中定义方法

    @interface Employee: NSObject
    
    //...
    
    + (int) max;
    + (void) setMax: (int)newValue;
    
    //...
    @end
    

    在.m文件中实现方法

    @implementation Employee {
    //...
    
    //为静态变量提供访问器方法
    + (int) max {
        return _max;
    }
    
    + (void) setMax: (int)newValue {
        _max = newValue;
    }
    
    //...
    }
    

    调用方法:

    //Employee为类型,不是对象
    [Employee setMax: 300];
    Employee.max = 400;
    NSLog(@"class variable is %d.", Employee.max);
    

    实例变量

    可以定义实例变量,而不定义属性。只有实例变量,没有类变量。

    如果同时自定义了getter和setter访问器方法,或者针对只读属性定义了getter访问其方法,编译器将不再合成实例变量。

    在类外一律使用属性来访问,类内大多也通过self使用属性访问。只有一下情况使用实例变量来访问:

    • 初始化器 init
    • 析构器 dealloc
    • 自定义访问器方法
    • 实例变量只能在类内访问,类外不可访问。
    • 引用类型的属性使用实例变量访问时可能会�有内存管理的问题。

    实例变量的生存周期

    实例变量的存储:跟随对象实例存储在堆上。

    值类型实例变量直「内嵌」在对象实例中。跟随对象实例内存释放而被释放。

    引用类型实例变量通过指针「引用」堆上的引用类型实例,ARC针对引用进行计数管理,自动释放引用计数为0的对象。

    属性的描述特性

    属性描述特性(Attribute)可以指定属性不同环境下的不同功能。

    • 读写特性
      • 读写属性 readwrite (默认)
      • 只读属性 readonly
    • 多线程特性
      • 原子性 atomic (默认)
      • 非原子性 nonatomic
    • 内存管理特性
      • ARC环境
        • 强引用 strong (默认)
        • 弱引用 weak 阻止循环引用
        • 拷贝属性 copy 为属性赋值时创建独立拷贝
      • 其他情况
        • retain
        • assign
        • unsafe_unretained
    @property (readonly, nonatomic) NSString* firstName;
    
    • 避免循环引用:使用weak属性

    4. 函数成员:方法

    函数成员 Function Member 描述对象行为

    • 方法 Method
    • 初始化器 Init
    • 析构器 Dealloc

    认识方法 Method

    函数:代码段上的可执行指令序列

    • 全局函数(C语言函数)
    • 成员函数(Objective-C方法)

    方法是类的成员函数,表达实例行为或类型行为。

    所有方法默认为公有方法。没有private或protected方法。

    动态消息分发:方法调用通过运行时动态消息分发实现,在对象上调用方法又称「向对象发送消息」。

    实例方法或类型方法

    实例方法——表达实例行为,可以访问

    • 实例成员(实例属性、实例变量、实例方法)
    • 类型方法、静态变量

    类方法——表达类型行为,访问权限:

    • 可以访问:类型方法、静态变量
    • 不能访问:实例成员(实力属性、实例变量、实例方法)

    了解编译器背后对实例方法和类方法的不同处理:self指针

    //实例方法
    - (void) print {
        NSLog(@"[%d, %d]", self.x, self.y);
    }
    //编译器编译后:
    void print(BLNPoint* self) {
        NSLog(@"[%d, %d]", self.x, self.y);
    }
    
    //调用时:
    [p1 print];
    //编译器编译后:
    print(p1);
    
    //类型方法
    + (BLNPoint*) getOriginPoint {
        //...
    
        //在类型方法中,self 等同于类型,与实例方法中的self不同。
        [self process];
        //等同于
        [BLNPoint process];
    
        [self print];//错误
    }
    //编译器编译后:
    BLNPoint* getOriginPoint() {
        //...
    }
    
    //调用时:
    BLNPoint* origin = [BLNPoint getOriginPoint];
    //编译器编译后:
    BLNPoint* origin = getOriginPoint();
    

    方法参数

    • 如果参数类型为值类型,则为传值方式;如果参数类型为引用类型,则为传指针方式。
    • 方法可以没有参数,也可以没有返回值。
    • 如果方法有参数,方法名约定包含第一个参数名,第二个参数开始需要显示提供外部参数名。
    • 调用时,第一个参数名忽略,但后面的参数名必须显式标明。
    - (BOOL) isEqualToPoint: (BLNPoint*) point;
    - (void) moveToX: (int)x toY: (int)y;
    
    //调用
    [p1 isEqualToPoint: p2];
    [p1 moveToX: 100 toY: 200];
    
    • id可以表示所有的类型
    id obj = [[BLNPoint alloc] init];
    [obj moveToX: 50 toY: 60];
    [obj print];
    [obj setX: 70];
    obj.x = 70;//obj声明为id时不可以这样用
    
    • 理解动态方法调用机制——消息分发表

    5. 初始化器与析构器

    认识初始化器与析构器

    初始化器用于初始化对象实例或者类型,是一个特殊的函数。

    • 对象初始化器:- (id) init 可以重载多个
    • 类型初始化器:+ (void) initialize 只能有一个

    析构器用于释放对象拥有的资源,无返回值的函数。

    • 对象析构器 - (void) dealloc 只能有一个
    • 没有类型析构器
    - (id) init;
    - (id) initWithName: (NSString *)name;
    - (id) initWithName: (NSString *)name WithPages:  (int)pages;
    - (void) dealloc;
    + (void) initialize;
    

    对象初始化器

    初始化对象实例时,init通常和alloc搭配使用。

    alloc所做的事情——NSObject已实现:

    1. 在堆上分配合适大小的内存。
    2. 将属性或者实例变量的内存置0。

    init所做的事情——可以自定义:

    1. 调用父类初始化器 [super init] (前置调用)。
    2. 初始化当前对象实例变量(注意使用实例变量,不要使用属性)。

    new 相当于调用alloc/init的无参数版本。

    - (id) init {
        self = [super init];
        if (self) {
            //...
        }
        return self;
    }
    - (id) initWithName: (NSString *)name WithPages:  (int)pages {
        self = [super init];
        if (self) {
            //初始化实例对象,使用实例变量,不要使用属性self.name
            _name = [name copy];
            _pages = pages;
        }
        return self;
    }
    
    
    //调用:
    
    Book* book = [Book new];
    //相当于
    Book* book = [[Book alloc] init];
    

    类初始化器

    类初始化器initialize负责类型级别的初始化。

    initialize在每个类使用之前被系统自动调用,且每个进程周期中,只被调用一次。

    子类的initialize会自动调用父类的initialize(前置调用)。

    + (void) initialize {
        if (self == [Book class]) {
            //...
        }
    }
    

    对象析构器

    对象析构器dealloc负责释放对象拥有的动态资源:

    • 自动实现:1. ARC将对象属性引用计数减持
    • 手动实现:2. 释放不受ARC管理的动态内存,如malloc分配的内存
    • 手动实现:3. 关闭非内存资源,如文件句柄、网络端口...

    dealloc由ARC根据对象引用计数规则,在释放对象内存前自动调用,无法手工调用。

    子类的dealloc会自动调用父类的dealloc(后置调用)。

    6. 继承与多态

    认识面向对象

    封装 Encapsulation

    隐藏对象内部实现细节,对外仅提供公共接口访问。

    继承 Inheritance

    一个类型在另外类型基础上进行的扩展实现。

    多态 Polymorphism

    不同类型针对同一行为接口的不同实现方式。

    认识继承 Inheritance

    继承:每一个类只能有一个基类,子类自动继承基类的:

    • 实例变量
    • 属性
    • 实例方法
    • 类方法

    了解所有类的根类:NSObject

    继承的两层含义:

    • 成员复用:子类复用基类成员
    • 类型抽象:将子类当做父类来使用(IS-A关系准则Circle is a Shape)
    • Objective-C只支持单继承,一个类只能有一个基类

    • 如果希望实例变量在类外可以访问,可以放在接口文件中,必须放在大括号内并加上@public,一般情况下不推荐

    @interface Shape: NSObject {
        @public int _data;
    }
    
    //调用时
    shape->_data++;
    

    认识运行时多态 Polymorphism

    多态:子类在父类统一行为接口下,表现不同的实现方式。

    对比重写与重载

    • 子类重写父类同名同参数方法:子类只可以重写override父类方法
    • 方法名相同、参数不同:Objective-C不支持方法的重载。

    在子类的代码中,可以使用super来调用基类的实现。

    • self具有多态性,可以指向不同子类
    • super,没有多态性,仅指向当前父类
    • 属性的setter和getter方法也可以被重写

    理解self的多态性:

    //Shape类
    - (void) draw {
        NSLog(@"Shape object draw");
    }
    - (void) move {
        NSLog(@"Shape object move");
        //这种情况下:self指代rectangle
        //所以会调用rectangle的draw方法
        [self draw];
    }
    
    //------------------------------
    
    //Rectangle类继承自Shape
    - (void) draw {
        NSLog(@"Rectangle object draw");
    }
    /**
    //可以理解为Rectangle有一个隐藏的move方法
    //和Shape类中的一模一样
    - (void) move {
        NSLog(@"Shape object move");
        //这种情况下:self指代rectangle
        //所以会调用rectangle的draw方法
        [self draw];
    }
    **/
    
    //----------------------------
    
    //调用时
    
    //Shape是声明类型,Rectangle是实际类型
    //所以这两行的执行结果是相同的
    Shape* rectangle = [[Rectangle alloc] init];
    //Rectangle* rectangle = [[Rectangle alloc] init];
    
    [rectangle draw];//Rectangle object draw
    
    //这里会输出:
    //Shape object move
    //Rectangle object draw
    [rectangle move];
    

    继承中的init和dealloc

    初始化器 init

    • 子类自动继承基类的初始化器
    • 子类也可以重写基类初始化器,此时子类初始化器必须首先调用基类的一个初始化器(手工调用)。

    析构器 dealloc

    • 子类可以选择继承基类析构器,或者重写基类析构器。
    • 子类析构器执行完毕后,会自动调用基类析构器(后置调用,且在开启ARC后不支持手工调用)
    • 子类析构器自动具有多态性

    Tips: 尽量避免在父类init和dealloc中调用子类重写的方法。

    - (id) init {
        self = [super init];
        if (self) {
            //...
        }
        return self;
    }
    

    相关文章

      网友评论

          本文标题:[GeekBand]Objective-C编程语言第一周笔记

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