OC大补之法

OC是Objective-C的简称,前面有说过想玩玩IOS的,那么肯定少不了OC语言啦,从维基百科上面了解了下OC,今天想和大家分享分享。这里主要是OC和Java进行一个对比,语言都是想通的。

Objective-C是一种通用、高级、面向对象的编程语言。它扩展了标准的ANSI C编程语言,将Smalltalk式的消息传递机制加入到ANSI C中。目前主要支持的编译器有GCC和LLVM(采用 Clang作为前端)。
Objective-C的商标权属于苹果公司,苹果公司也是这个编程语言的主要开发者。苹果在开发NeXTSTEP操作系统时使用了Objective-C,之后被OS X和iOS继承下来。现在Objective-C是OS X和iOS操作系统、及与其相关的API、Cocoa和Cocoa Touch的主要编程语言。

历史

  • Cox 撰写了一个 C 语言的预处理器,打算使 C 语言具备些许 Smalltalk 的本领。Cox 很快地实现了一个可用的 C语言扩展,此即为 Objective-C语言的前身。
  • 到了 1983 年,Cox 与 Love 合伙成立了 Productivity Products International(PPI)公司,将 Objective-C 及其相关库商品化贩售,并在之后将公司改名为StepStone。
  • 1992年,自由软件基金会的 GNU 开发环境增加了对 Objective-C的支持。
  • 1994年,NeXT Computer公司和Sun Microsystem联合发布了一个针对 NEXTSTEP 系统的标准典范,名为OPENSTEP。OPENSTEP 在自由软件基金会的实现名称为 GNUstep。
  • 1996年12月20日,苹果公司宣布收购 NeXTSoftware 公司,NEXTSTEP/OPENSTEP环境成为苹果操作系统下一个主要发行版本OSX的基础。这个开发环境的版本被苹果公司称为Cocoa。
  • 2005年,苹果电脑雇用了克里斯·拉特纳及LLVM开发团队,clang及LLVM成为苹果公司在GCC之外的新编译器选择,在 Xcode4.0之后均采用 LLVM 作为默认的编译器。最新的 Objective-C 特性也都率先在 Clang 上实现。

语法

Objective-C是C语言的严格超集--任何C语言程序不经修改就可以直接通过Objective-C编译器,在Objective-C中使用C语言代码也是完全合法的。Objective-C的原意就是在C语言主体上加入面向对象的特性。所有其他非面向对象的语法,包括变数类型,预处理器(preprocessing),流程控制,函数声明与调用皆与C语言完全一致。但有些C语言语法合法代码在objective-c中表达的意思不一定相同,比如某些布尔表达式,在C语言中返回值为true,但在Objective-C若与yes直接相比较,函数将会出错,因为在Objective-C中yes的值只表示为1.
字符串
Objective-C里有字符串是由双引号包裹,并在引号前加一个@符号,例如:

title = @"Hello";
if(title == @"hello") {}

Java里面

String title = "Hello";
if(title =="Hello"){}

HelloWorld
这里示范了一个基础的Hello World程序

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {

    @autoreleasepool {
        NSLog(@"Hello World!");
    }

   return 0;
}

方法对比

Objective-C语言:

-(void) HelloWorld:(BOOL)ishelloworld{  
//干点啥  
}  

Java语言

public void helloWorld(bool ishelloworld) {  
//干点啥  
}  

函数调用
不涉及面向对象时,它和C是完全一样的。以下是几个函数调用的示例:

  • 不带参数
startedBlock();
  • 带参数
NSLog(@"decrypted string: %@", str);
CGRectMake(0,0,0,0);

消息传递

Objective-C最大的特色是承自Smalltalk的消息传递模型(message passing)。Objective-C里,与其说对象互相调用方法,不如说对象之间互相传递消息更为精确。此二种风格的主要差异在于调用方法/消息传递这个动作。Java里类与方法的关系严格清楚,一个方法必定属于一个类,而且在编译时(compile time)就已经紧密绑定,不可能调用一个不存在类里的方法。但在Objective-C,类与消息的关系比较松散,调用方法视为对对象发送消息,所有方法都被视为对消息的回应。所有消息处理直到运行时(runtime)才会动态决定,并交由类自行决定如何处理收到的消息。也就是说,一个类不保证一定会回应收到的消息,如果类收到了一个无法处理的消息,程序只会抛出异常,不会出错或崩溃。
Java里,送一个消息给对象(或者说调用一个方法)的语法如下:

  • 不带参数
[obj method];

Java版本

obj.method();
  • 带参数
  • -
[counter increase:1];

Java版本

counter.increase(1);
  • 带多个参数
    对C Family程序员来说,这是最难接受的,最反人类的:
- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue {...} //定义方法
[myObj setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; //调用方法

Java版本

public void setColorToRedGreenBlue(float red, float green, float blue) {...}
myObj.setColorToRedGreenBlue(1.0, 0.8, 0.2);

此二者并不仅仅是语法上的差异,还有基本行为上的不同。

这里以一个汽车类(car class)的简单例子来解释Objective-C的消息传递特性:

[car fly];

典型的Java意义解读是“调用car类的fly方法”。若car类里头没有定义fly方法,那编译肯定不会通过。但是
Objective-C里,我们应当解读为“发提交一个fly的消息给car对象”,fly是消息,而car是消息的接收者。
car收到消息后会决定如何回应这个消息,若car类内定义有fly方法就运行方法内之代码,若car内不存在fly方法,则程序依旧可以通过编译,运行期则抛出异常。

此二种风格各有优劣。Java强制要求所有的方法都必须有对应的动作,且编译期绑定使得函数调用非常快速。缺点是仅能借由某些l关键字提供有限的动态绑定能力。Objective-C天生即具备鸭子类型之动态绑定能力,因为运行期才处理消息,允许发送未知消息给对象。可以送消息给整个对象集合而不需要一一检查每个对象的型态,也具备消息转送机制。同时空对象nil接受消息后默认为不做事,所以送消息给nil也不用担心程序崩溃。

  • 消息的嵌套
UINavigationBar *bar = [[[UINavigationBar alloc] init] autorelease];

Java版本

UINavigationBar bar = UINavigationBar.alloc().init().autorelease();//Java没有指针,所以星号去掉了


接口和实现
Objective-C的类分为接口定义和实现两个部分。接口定义(Interface)放在头文件中,文件扩展名是.h,实现(implementation)放在实现文件中,文件扩展名是.m(也有.mm的扩展名,表示Objective-C和C++混编的代码)。

接口定义也可以写在.m文件中,但最好不要这么干

需要注意的是,与Objective-C的interface概念最接近的是C和C++里的头文件,它与implementation是成双成对出现的,作用是声明类的成员变量和方法。它与Java的interface概念完全不同:

  1. Objective-C里,interface有且只有一个实现,Java的interface可以有0-N个实现
  2. Objective-C里,interface可以定义成员属性,Java里不可以

在Objective-C里,和Java的Interface概念相似的是Protocol,下文会讲到。

请看示例:
Interface

@interface MyClass {
    int memberVar1;
    id  memberVar2;
}

-(return_type) instance_method1; 
-(return_type) instance_method2: (int) p1;
-(return_type) instance_method3: (int) p1 andPar: (int) p2;
@end

Implementation

@implementation MyClass {
    int memberVar3;
}

-(return_type) instance_method1 {
    ....
}
-(return_type) instance_method2: (int) p1 {
    ....
}
-(return_type) instance_method3: (int) p1 andPar: (int) p2 {
    ....
}
@end

接口和实现以@interface、@implementation开头,都以@end结束。“@”符号在Objective-C中是个很神奇的符号。

冒号也是方法名的一部分,method和method:是两个不同的方法名,不是overload,第二个带参数。

上述代码对应的Java版:

public class MyClass {
    protected int memberVar1;
    protected pointer memberVar2;
    private int memberVar3;

    public (return_type) instance_method1() {
        ....
    }

    public (return_type) instance_method2(int p1) {
        ....
    }

    public (return_type) instance_method3andPar(int p1, int p2) {
        ....
    }
}

Objective-C定义一个新的方法时,名称内的冒号(:)代表参数传递,不同于C语言以数学函数的括号来传递参数。Objective-C方法使得参数可以夹杂于名称中间,不必全部附缀于方法名称的尾端,可以提高程序可读性。
值得一提的是不只Interface区段可定义实体变数,Implementation区段也可以定义实体变数,两者的差别在于访问权限的不同,Interface区段内的实体变数默认权限为protected,声明于implementation区段的实体变数则默认为private,故在Implementation区段定义私有成员更符合面向对象之封装原则,因为如此类之私有信息就不需曝露于公开interface(.h文件)中。

创建对象

Objective-C创建对象需通过alloc以及init两个消息。alloc的作用是分配内存,init则是初始化对象。 init与alloc都是定义在NSObject里的方法,父对象收到这两个信息并做出正确回应后,新对象才创建完毕。以下为范例:

MyObject * my = [[MyObject alloc] init];

在Objective-C 2.0里,若创建对象不需要参数,则可直接使用new

MyObject * my = [MyObject new];

仅仅是语法上的精简,效果完全相同。

若要自己定义初始化的过程,可以重写init方法,来添加额外的工作。(用途类似于构造函数)

- (id) init {
    if ( self=[super init] ) {   // 必须调用父类的init
        // do something here ...
    }
    return self;
}

私有方法和公开方法
写在.h头文件里的方法都是公开的,Objective-C里没有私有方法的概念(没有你说个蛋啊,哈哈哈哈)。

官方并没有提到Objective-C怎么实现私有方法,我查阅了stackoverflow,统一的答案是,要实现私有方法的效果只能借助Category,不过,根据我的测试,即使采用了Category,也不能阻止外部的代码调用这个“私有方法”,只是Xcode不支持“私有方法”的自动完成,并会有警告提示,运行的时候,还是会成功的。各位看官知道有这么回事就可以了,这里不深讲。

变量和属性

  • 类方法
    类方法就是Java、PHP里的Static Method,不用实例化就能调。类方法有一个加号前缀。 示例:

类定义

@interface MyClass
    +(void) sayHello;
@end

@implementation MyClass

+(void) sayHello {
    NSLog(@"Hello, World");
}
@end

使用

[MyClass sayHello];
  • 实例方法
    实例方法就是Java、PHP里的普通方法,必须实例化才能调。实例方法有一个减号前缀。 示例:

类定义

@interface MyClass : NSObject
-(void) sayHello;
@end

@implementation MyClass

-(void) sayHello {
    NSLog(@"Hello, World");
}
@end

使用

mycls = [MyClass new];
[mycls sayHello];
  • Selector
    selector就是一个方法指针,类似PHP里的动态方法名:
<?php
class Hello {
    public function sayHello() {}

    public function test() {
        $fun_name = "sayHello";
        $this->$fun_name();
    }
}

在Objective-C里,selector主要用来做两类事情:
绑定控件触发的动作

@implementation DemoViewController
- (void)downButtonPressed:(id)sender {//响应“按钮被按下事件”的方法
    UIButton *button = (UIButton*)sender;
    [button setSelected:YES];
}

- (void)drawAnButton {
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 
    btn.frame = _frame; 
    btn.tag = 1;
    btn.backgroundColor = [UIColor clearColor];
    [btn addTarget: self
         action: @selector(downButtonPressed:)
         forControlEvents: UIControlEventTouchUpInside];//当这个按钮被按下时,触发downButtonPressed:方法
}
@end

延时异步执行

@implementation ETHotDealViewController
- (void)viewDidLoad {

    //获取数据源
    HotDealDataSource *ds = [[HotDealDataSource alloc]init];
    [ds reload];
    _items = ds.items;

    [self performSelector: @selector(refreshTable)
          withObject: self
          afterDelay: 0.5];//延迟0.5秒调用refreshTable方法
}

-(void)refreshTable
{
    [self.tableView reloadData];
}
@end

这个例子中,获取数据源是通过ASIHTTP组件异步调用服务端HTTP接口,refreshTable要用到数据源返回回来的数据,如果不延迟0.5秒,就会立刻执行,执行的时候数据还在路上呢,页面就要变空白了。

  • 继承
    继承是写在Interface定义里面的。语法为:子类名在左,父类名在右,中间用冒号分隔。 示例:
@interface MyClass : NSObject
@end

对应的Java版本是:

public class MyClass extends NSObject {
}

协议(Protocol)

协议是一组没有实现的方法列表,任何的类均可采纳协议并具体实现这组方法。
Objective-C在NeXT时期曾经试图引入多重继承的概念,但由于协议的出现而没有实现之。
协议类似于Java与C#语言中的“接口”。在Objective-C中,有两种定义协议的方式:由编译器保证的“正式协议”,以及为特定目的设定的“非正式协议”。

非正式协议为一个可以选择性实现的一系列方法列表。非正式协议虽名为协议,但实际上是挂于NSObject上的未实现分类(Unimplemented Category)的一种称谓,Objetive-C语言机制上并没有非正式协议这种东西,OSX 10.6版本之后由于引入@optional关键字,使得正式协议已具备同样的能力,所以非正式协议已经被废弃不再使用。

正式协议类似于Java中的”接口”,它是一系列方法的列表,任何类都可以声明自身实现了某个协议。在Objective-C 2.0之前,一个类必须实现它声明符合的协议中的所有方法,否则编译器会报告错误,表明这个类没有实现它声明符合的协议中的全部方法。Objective-C 2.0版本允许标记协议中某些方法为可选的(Optional),这样编译器就不会强制实现这些可选的方法。

协议经常应用于Cocoa中的委托及事件触发。例如文本框类通常会包括一个委托(delegate)对象,该对象可以实现一个协议,该协议中可能包含一个实现文字输入的自动完成方法。若这个委托对象实现了这个方法,那么文本框类就会在适当的时候触发自动完成事件,并调用这个方法用于自动完成功能。

Objective-C中协议的概念与Java中接口的概念并不完全相同,即一个类可以在不声明它符合某个协议的情况下,实现这个协议所包含的方法,也即实质上符合这个协议,而这种差别对外部代码而言是不可见的。正式协议的声明不提供实现,它只是简单地表明符合该协议的类实现了该协议的方法,保证调用端可以安全调用方法。

  • 语法

协议以关键字@protocol作为区段起始,@end结束,中间为方法列表。

@protocol Locking
- (void)lock;
- (void)unlock;
- (void)print:(NSString)str;
@end

Java版本

public interface Locking{
public void lock();
public void unlck();
public void print(String str);
}

这是一个协议的例子,多线程编程中经常要确保一份共享资源同时只有一个线程可以使用,会在使用前给该资源挂上锁 ,以上即为一个表明有“锁”的概念的协议,协议中有两个方法,只有名称但尚未实现。
- 协议的继承
协议本身也可以继承别的协议:

@protocol Printable <NSObject>
    -(void)print:(NSString)str;
@end

对应的Java版本:

public interface Printable extends NSObject {
    public void print (String str);
}
  • 可选方法
    协议可以包含可选方法,顾名思义,可选方法可以不被类实现:
@protocol Printable
@optional
    -(void)print:(NSString)str;
@end

加了@optional关键字,一个类在implements这个协议时,便可以不实现print:方法。

Java里没有类似的实现,除了Collection里会有一些方法带有optional的注释,但Collection是个特例。
- 协议的实现
一个类实现某些协议是写在Interface定义里面的。语法为:协议名用尖括号包裹,多个协议名用逗号隔开,协议写在父类的右边(如果没有父类就直接写在子类右边)。

示例:

@interface  class MyClass : NSObject <Printable, Drawable>
@end

Printable, Drawablw就是两个协议。

对应的Java版本是:

public class MyClass extends NSObject implements Printable, Drawable {
}

下面的SomeClass宣称他采纳了Locking协议:

@interface SomeClass : SomeSuperClass <Locking>
@end

一旦SomeClass表明他采纳了Locking协议,SomeClass就有义务实现Locking协议中的两个方法.

@implementation SomeClass
- (void)lock {
  // 實現lock方法...
}
- (void)unlock {
  // 實現unlock方法...
}
@end

动态类型

类似于Smalltalk,Objective-C具备动态类型:即消息可以发送给任何对象实体,无论该对象实体的公开接口中有没有对应的方法。对比于C++这种静态类型的语言,编译器会挡下对(void*)指针调用方法的行为。但在Objective-C中,你可以对id发送任何消息(id很像void*,但是被严格限制只能使用在对象上),编译器仅会发出“该对象可能无法回应消息”的警告,程序可以通过编译,而实际发生的事则取决于运行期该对象的真正形态,若该对象的确可以回应消息,则依旧运行对应的方法。

一个对象收到消息之后,他有三种处理消息的可能手段,第一是回应该消息并运行方法,若无法回应,则可以转发消息给其他对象,若以上两者均无,就要处理无法回应而抛出的例外。只要进行三者之其一,该消息就算完成任务而被丢弃。若对“nil”(空对象指针)发送消息,该消息通常会被忽略,取决于编译器选项可能会抛出例外。

虽然Objective-C具备动态类型的能力,但编译期的静态类型检查依旧可以应用到变量上。以下三种声明在运行时效力是完全相同的,但是三种声明提供了一个比一个更明显的类型信息,附加的类型信息让编译器在编译时可以检查变量类型,并对类型不符的变量提出警告。

下面三个方法,差异仅在于参数的形态:

- setMyValue:(id) foo;

id形态表示参数“foo”可以是任何类的实例。

- setMyValue:(id <aProtocol>) foo;

id< aProtocol>表示“foo”可以是任何类的实例,但必须采纳“aProtocol”协议。

- setMyValue:(NSNumber*) foo;

该声明表示“foo”必须是“NSNumber”的实例。

动态类型是一种强大的特性。在缺少泛型的静态类型语言(如Java 5以前的版本)中实现容器类时,程序员需要写一种针对通用类型对象的容器类,然后在通用类型和实际类型中不停的强制类型转换。无论如何,类型转换会破坏静态类型,例如写入一个“整数”而将其读取为“字符串”会产生运行时错误。这样的问题被泛型解决,但容器类需要其内容对象的类型一致,而对于动态类型语言则完全没有这方面的问题。

转发
Objective-C允许对一个对象发送消息,不管它是否能够响应之。除了响应或丢弃消息以外,对象也可以将消息转发到可以响应该消息的对象。
Objective-C运行时在Object中定义了一对方法:
1)转发方法

- (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC
- (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

2)响应方法

- (retval_t) performv:(SEL) sel :(arglist_t) args;  // with GCC
- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

希望实现转发的对象只需用新的方法覆盖以上方法来定义其转发行为。无需重写响应方法performv::,由于该方法只是单纯的对响应对象发送消息并传递参数。其中,SEL类型是Objective-C中消息的类型。
下面是一个转发的例子
Forwarder.h

#import <objc/Object.h>

@interface Forwarder : Object
{
    id recipient; //该对象是我们希望转发到的对象。
}

@property (assign, nonatomic) id recipient;

@end

Forwarder.m

#import "Forwarder.h"

@implementation Forwarder

@synthesize recipient;

- (retval_t) forward: (SEL) sel : (arglist_t) args
{
    /*
     *检查转发对象是否响应该消息。
     *若转发对象不响应该消息,则不会转发,而产生一个错误。
     */
    if([recipient respondsTo:sel])
       return [recipient performv: sel : args];
    else
       return [self error:"Recipient does not respond"];
}

Recipient.h

#import <objc/Object.h>

// A simple Recipient object.
@interface Recipient : Object
- (id) hello;
@end

Recipient.m

#import "Recipient.h"

@implementation Recipient

- (id) hello
{
    printf("Recipient says hello!\n");

    return self;
}

@end

main.m

#import "Forwarder.h"
#import "Recipient.h"

int main(void)
{
    Forwarder *forwarder = [Forwarder new];
    Recipient *recipient = [Recipient new];

    forwarder.recipient = recipient; //Set the recipient.
    /*
     *转发者不响应hello消息!该消息将被转发到转发对象。
     *(若转发对象响应该消息)
     */
    [forwarder hello];

    return 0;
}

分类
分类可以给一个已经存在的类增加方法,而不用去改它的源码。Java和PHP中都没有类似的特性。

比如说,NSObject是一个Objective-C内置的系统类,我们想给它增加toJson方法,就像这样:

头文件:NSObject+Json.h

@interface NSObject (Json)
    -(NSString)toJson;
@end

实现文件:NSObject+Json.m

@implementation NSObject (Json)
    -(NSString)toJson {
        //...
    }
@end

使用的时候,只要包含NSObject+Json.h,实例化NSObject类,就可以使用toJson方法了:

import "NSObject+Json.h"
@implatementation XYZController
    -(void)test {
        NSObject *obj = [[NSObject alloc]init];
        NSString *str = [obj toJson];
    }
@end

当然了,NSObject本来的那些方法依然还是可以用的,什么都没变,只是多了个toJson方法。看起来是不是和继承没太多差别呢(除了使用的时候实例化的是NSObject,而不是JsonObject)?再看一个继承实现不了的例子:

头文件:NSObject+Json+XML.h

@interface NSObject (Json)
    -(NSString)toJson;
@end

@interface NSObject (XML)
    -(NSString)toXML;
@end

实现文件:NSObject+Json+XML.m

@implementation NSObject (Json)
    -(NSString)toJson {
        //...
    }
@end

@implementation NSObject (XML)
    -(NSString)toXML {
        //...
    }
@end

使用:

import "NSObject+Json+XML.h"
@implatementation XYZController
    -(void)test {
        NSObject *obj = [[NSObject alloc]init];
        NSString *json = [obj toJson];
        NSString *xml = [obj toXML];
    }
@end

没有头绪,没有想法的写完了,格式比较乱,MarkDown的语言以及排版是时候看看了,后面有用到的东西,有遇到的还会录取添加的。。。。。努力中~~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值