码迷,mamicode.com
首页 > 移动开发 > 详细

iOS完全自学手册——[三]Objective-C语言速成,利用Objective-C创建自己的对象

时间:2016-01-11 01:30:17      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:

1.前言

上一篇已经介绍了App Delegate、View Controller的基本概念,除此之外,分别利用storyboard和纯代码创建了第一个Xcode的工程,并对不同方式搭建项目进行了比较。这一篇文章,我准备为大家介绍一下奇葩的Objective-C语言的语法和使用。这篇文章主要讲Objective-C的理论概念。

2.学习目标

2.1 Objective-C语言面向对象的特性与其他语言(C++)类比学习,声明定义一个MyObject类,并创建这个类的实例。

2.2 Objective-C语言常见语法概念概览。[协议,分类,扩展,消息机制,动态特性selector,KVC,KVO,block,ARC]

3.奇葩的Objective-C语言

对于已经有开发经验的开发者而言,初次接触Objective-C的时候觉得根本不能接受,甚至有种畏惧,oc奇葩的语法简直颠覆了自己的世界观,因为出现了莫名其妙的符号,如, + - [ ]。但是,不要怕,我们慢慢来了解这到底是毛线。

首先,从名字即可看出,Objective-C是一个面向对象的编程语言,也就是它和其他面向对象的语言一样有继承、封装、多态的语言特性,学习的时候可以类比其他语言。当然,如果没接触过其他语言也没关系,那就直接学。那么,就用C++和Objective-C两种语言去声明定义一个MyObject类。

3.1 先看一下C++中声明定义类是如何编写的

3.1.1 C++程序源码

#include <iostream>
using namespace std;
class MyObject {
public:
    MyObject();
    ~MyObject();
    void display();
    void setValue(int x);
    int getIndex();
    int getKey();
    static void classMethod();
private:
    int index;
    int key;
};

MyObject::MyObject() {
    cout<<"construct"<<endl;
}

MyObject::~MyObject() {
    cout<<"destruct"<<endl;
}

void MyObject::setValue(int x) {
    this->index = x;
    this->key = 1<<(x+2);
}

void MyObject::display() {
    cout<<"index == "<<this->index<<endl;
    cout<<"key == "<<this->key<<endl;
}

int MyObject::getKey() {
    return this->key;
}

int MyObject::getIndex() {
    return this->index;
}

void MyObject::classMethod() {
    cout<<"this is static (or class) method"<<endl;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    
    MyObject *object = new MyObject();
    MyObject::classMethod();
    object->setValue(3);
    object->display();
    int c = object->getIndex() + object->getKey();
    object->MyObject::~MyObject();
    printf("c = %d\n", c);
    
    MyObject object2;
    object2.setValue(2);
    object2.display();
    
    return 0;
}

3.1.2 控制台打印信息

Hello, World!

construct

this is static (or class) method

index == 3

key == 32

destruct

c = 35

construct

index == 2

key == 16

destruct

Program ended with exit code: 0

3.1.3 代码解释:

以上代码利用C++声明定义了MyObject类

1)定义了公有方法方法

  • 构造函数MyObject()
  • 析构函数~MyObject()
  • 打印index和key的方法display()
  • 设置私有变量方法setValue(int x)
  • 获得index值方法getIndex()
  • 获得key方法getKey()
  • 类方法classMethod()

2)定义了私有成员变量

  • index和key

3.1.4 特别注意

  • 构造函数是创建实例时调用的,在这个方法中可以为实例进行初始化操作;
  • 析构函数是销毁实例对象是被系统调用的,这里可以将对象中的指针对象都delete掉;
  • 不管是否使用new关键字创建对象,系统都会自动调用构造函数和析构函数,构造函数实质是为对象在内存分配一个空间,存储这个对象的数据,而析构函数则是将这个对象占用的内存释放掉
  • 使用new关键字创建对象时,调用对象的方法(或成员变量)使用“->”符号;不使用new关键字创建对象是,调用对象须使用“.”符号。
  • 类方法是静态方法,是在类装载的时候装载的。(但是要特别注意,类的静态变量是该类的对象所共有的,即是所有对象共享变量。所以建议尽量少用静态变量。尽量在静态方法中使用内部变量。)

 

3.2 再看一下Objective-C是如何定义一个MyObject类的

3.2.1 创建新项目

选择OS X中的Command Line Tool,创建好工程后,按住command+N键创建Cocoa Touch Class,创建一个名为MyObject的NSObject的子类。

技术分享

技术分享

技术分享

这样就创建了OS X的命令行程序,以用于简单演示如何声明定义Objective-C的类

3.2.2 程序源码

1)main.m文件:

#import <Foundation/Foundation.h>

#import "MyObject.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        MyObject *object = [[MyObject alloc] initWithIndex:3];
        [object display];
//        object = nil;
    }
    return 0;
}

 2) MyObject.h

#import <Foundation/Foundation.h>

@interface MyObject : NSObject
- (instancetype)initWithIndex:(NSInteger)index;
- (void)display;
+ (void)classMethod;
@end

3) .m文件

#import "MyObject.h"

@interface MyObject()
@property (assign, nonatomic) NSInteger index;
@property (assign, nonatomic) NSInteger key;

@end

@implementation MyObject
@synthesize index = _index;
@synthesize key = _key;

- (instancetype)initWithIndex:(NSInteger)index {
    self = [super init];
    if (self) {
        NSLog(@"init");
        self.index = index;
    }
    return self;
}

- (void)setIndex:(NSInteger)index {
    _index = index;
    self.key = 1<<(index+2);
}

- (NSInteger)index {
    return _index;
}

- (NSInteger)key {
    return _key;
}

- (void)display {
    NSLog(@"index == %ld \n key == %ld", (long)_index, (long)_key);
}

+ (void)classMethod {
    NSLog(@"this is static (or class) method");
}

- (void)dealloc {
    NSLog(@"dealloc");
}
@end

 

3.2.3 控制台打印结果

2016-01-10 17:55:50.401 CompileOC[20792:1052939] Hello, World!

2016-01-10 17:55:50.402 CompileOC[20792:1052939] init

2016-01-10 17:55:50.402 CompileOC[20792:1052939] index == 3 

 key == 32

2016-01-10 17:55:50.402 CompileOC[20792:1052939] dealloc

Program ended with exit code: 0

3.2.4 代码解释

1) 在main.m文件中创建了MyObject的实例并调用display方法。

2)在头文件中声明了三个公有方法,

- (instancetype)initWithIndex:(NSInteger)index;

- (void)display;

+ (void)classMethod;

3).m文件中

  • 创建了一个MyObject类的扩展,在扩展中声明了两个NSInteger的属性,属性的特性为assign和nonatomic
  • 重写了父类中的实例(减号)init方法,并为index属性赋值
  • 实现了index属性的setter方法
  • 实现了index和key属性的getter方法
  • 创建了打印index和key的方法

    - (void)display

  • 创建了类方法

    + (void)classMethod;

  • 重写了父类的dealloc方法

     

4)与C++代码做对比

  • 在C++中可以创建没有父类的类,但是在Objective-C中创建的类都继承自NSObject或其子类。不选择父类的话是不能创建Objective-
  • 在C++中调用方法或变量用“->”或“.”符号,而在Objective-C中调用方法是[],而且顺序不是从左至右,而是从内到外
  • 在C++中声明公有方法用public关键字表明, 私有方法用private表明。而在Objective-C中声明公有方法只要在头文件的

    @interface

    @end
    之间就可以了,而私有方法不需要在头文件中声明。

  • 补充,在Objective-C中可以使用@private @public @protected @package 编译器指令来设置实例变量的访问限制。
    实例变量的声明方式为,

    @implementation {
            @private
                  int index;
                  int key;
    }
    @end
      

  • 注意实例变量和类的属性并不是一回事,属性和实例变量的本质区别是,属性无法直接访问对象的内部状态,但是可以利用setter和getter方法进行访问,而在setter和getter方法中还可以包含其他逻辑,比如,本文章中

    - (void)setIndex:(NSInteger)index
    方法,不单为实例变量
    _index赋值,还用setter的简洁方式

    self.key = 1<<(index+2);
    为实例变量_key赋值。

  • 那_index和_key实例变量是谁声明的呢?这是Xcode编译器自己生成的:

    @synthesize index = _index;

    @synthesize key = _key;
    上面的代码使用了 @synthesize 编译指令,让编译器分别为属性index和key自动创建实例变量_index和_key。即,自动为属性创建对应实例变量的方法是 
    @synthesize 属性名 = 实例变量名;
    事实上,当没有实现setter方法时,@synthesize指令不写,编译器也可以为属性自动创建实例变量,编译器自动为属性创建的变量名默认为_属性名,在调试的时候可以设置变量观察。

  • 注意本程序是在Xcode7中创建运行的,Xcode5之后创建工程时就默认ARC模式了,现在基本项目都是自动引用计数的模式编写的。老项目中的使用手动管理的需要做如下的设置:
    在targets的build phases选项下Compile Sources下选择要不使用arc编译的文件,双击它,输入-fno-objc-arc即可
  • 在项目开发中,我认为属性远比实例变量好用,大可以只声明属性,而不声明实例变量。

4.Objective-C语言常见语法概念

4.1 基本语法概念

4.1.1类元素
1)实例变量

声明方法:

@implementation {
       //声明实例变量
}
@end

实例变量是是类封装的数据

2)属性

@property (特性) 类型 属性名;

属性是Objective-C提供的访问对象共享状态的机制,编译器可以自动生成访问实例变量的方法。Xcode自动创建的实例变量默认变量名为“_属性名”

3)类的声明(接口)

@interface MyObject : NSObject

@end

通用的如下:

@interface 类名 : 父类名

@end

说明:

  • @interface 和 @end 指令之间的区域只能声明属性或方法,不能有具体的实现过程。
  • 如果在头文件中的接口声明的属性或方法,其实就是公有的属性和方法,可以直接访问。

4)类的实现

@implementation {

//实例变量的声明

}

//属性和方法的定义

@end

说明:

  • 方法的实现过程必须写在 @implementation 和 @end 之间

5)类的方法:分为类方法和实例方法。

  • 声明:

    [+或-](返回类型)方法名:(参数类型)参数1 参数名:(参数类型)参数2 ... 参数名:(参数类型)参数n;
  • 实现:

    [+或-](返回类型)方法名:(参数类型)参数1 参数名2:(参数类型)参数2 ... 参数名n:(参数类型)参数n {

                  //code here

                 return 返回值;

         }

  • 调用:
    [消息接收者(即类的实例对象) 方法名:1  参数名2:2 ...  参数名n:n];

说明:

  • 方法的声明其实就是方法部分实现除了{}以外的代码
  • Objective-C的方法定义方式很奇葩,刚开始不好接受,这其实是Objective-C独有的消息机制一种体现。
    • 在其他语言中,方法的书写方式是函数式的,类似于数学中的函数f(x,y,z)。
    • 在Objective-C中调用方法时向消息的接收者(类的实例对象)发送一条消息,书写形式为[消息接收者 消息方法名:参数1 参数名2:参数2],所有调用方法都是动态的,根据发出消息的方法名来动态查找对应的指针。感兴趣的话可以参考罗朝辉大神的博客 http://blog.csdn.net/kesalin/article/details/6689226

      “不同的类可以拥有相同的 selector,这个没有问题,因为不同类的实例对象performSelector相同的 selector 时,会在各自的消息选标(selector)/实现地址(address) 方法链表中根据 selector 去查找具体的方法实现IMP, 然后用这个方法实现去执行具体的实现代码。这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,只有在执行的时候,通过selector去查询,我们才能确定具体的执行代码。”

  • 多参数的声明、实现和调用。例如,我在MyObject中定义一个多参数的请求方法:

    - (void)requestDataWithIndex:(NSInteger)index forKey:(NSInteger)key completion:(void(^)(NSError *error, id response))completion {

        //code here

        NSError *responseError = nil;//assume it is responsed from server

        NSDictionary *responseData = @{@"message": @"OK"};

        if (completion != nil) {

            if (responseError != nil) {

                completion(responseError, nil);

            } else {

                completion(nil, responseData);

            }

        }

    }

    说明:
    • - (void)requestDataWithIndex:(NSInteger)index forKey:(NSInteger)key completion:(void(^)(NSError *error, id response))completion直接可以复制到@interface 和 @end 之间作为方法的声明
    • 注意方法中第三个参数是block,这个后面会稍作介绍,先了解概念混个眼熟,以后慢慢细琢磨。block是一块代码块,实质是闭包,程序运行时,block{}内的不会立即执行,即非顺序执行的,是异步执行的,所以最适合选为做网络异步请求的回调函数,功能类似于ajax。

6)类的扩展

其实就是写在类.m文件中的接口,只不过与类.h中接口相比,在.m文件中声明的实例变量、属性、方法外部不能访问,即实现了方法、属性的私有化。例如MyObject.m文件中

@interface MyObject()

@property (assign, nonatomic) NSInteger index;

@property (assign, nonatomic) NSInteger key;

@end

这段代码就是扩展,并且声明的index和key属性外部不能访问,只能在类的内部访问。

7)分类

分类的目的主要是为了扩展类的方法,比如MyObject类我添加一个NY的分类,创建方法如下:

  • command+N 选择iOS Source中的Objective-C File,Next
  • 填写File(分类名),选择category,class 填写MyObject,然后点Next,再点Create创建
  • 头文件:
#import "MyObject.h"

@interface MyObject (NY)

@end
  • 实现文件
#import "MyObject+NY.h"

@implementation MyObject (NY)

@end

说明:

  • 分类中只可以声明和定义方法,不可以声明类接口属性或实例变量。因为已经引用了原来类的头文件会报重复声明类的语法错误。

/Users/niyao/GitHub/demo/CompileOC/CompileOC/MyObject+NY.m:10:1: Duplicate interface definition for class ‘MyObject‘

 

8)协议

  • 协议的声明定义

@protocol NYProtocol <NSObject>

@optional

- (void)nyOptionalProtocalMethod;

@required

- (void)nyRequiredProtocalMethod;

@end

通用:

@protocol 协议名 <NSObject>

@optional

//可选择实现的方法

@required

//必须实现的方法

@end

  • 类遵从协议的方法

@interface MyObject() <NYProtocol>

@property (assign, nonatomic) NSInteger index;

@property (assign, nonatomic) NSInteger key; 

@end

说明:

  • 协议的定义可以写写在类的头文件中也可以写在类的实现文件中,或者也可以创建一个单独的协议头文件,但是必须要写在遵从该协议的类接口之前,否则会报语法错误
  • @optional指令标明的方法,遵从该协议的类不须要实现,但@required指令标明的必须实现
  • 只有类的接口后面可以跟<协议> @interface MyObject() <NYProtocol>

4.2 ARC(Automatic Reference Counting)

系统根据对象被引用的次数计次,当引用计数为零时,释放该对象所占有的内存。

4.3 KVC&KVO

 

博文推荐
http://blog.csdn.net/kesalin/article/details/8155245

iOS完全自学手册——[三]Objective-C语言速成,利用Objective-C创建自己的对象

标签:

原文地址:http://www.cnblogs.com/nycoder/p/5120043.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!