zoukankan      html  css  js  c++  java
  • Objective-C

    Objective-C代码的文件扩展名

    扩展名 内容类型
    .h 头文件。头文件包含类,类型,函数和常数的声明。
    .m 源代码文件。这是典型的源代码文件扩展名,可以包含 Objective-C 和 C 代码。
    .mm 源代码文件。带有这种扩展名的源代码文件,除了可以包含Objective-C和C代码以外还可以包含C++代码。仅在你的Objective-C代码中确实需要使用C++类或者特性的时候才用这种扩展名。

    当你需要在源代码中包含头文件的时候,你可以使用标准的 #include 编译选项,但是 Objective-C 提供了更好的方法。#import 选项和 #include 选项完全相同,只是它可以确保相同的文件只会被包含一次。Objective-C 的例子和文档都倾向于使用 #import,你的代码也应该是这样的。

    消息传递

    [obj method: argument] 对象调用方法,对象之间互相传递消息。

    字符串

    NSString类提供了字符串的类包装,包含了所有你期望的优点,包括对保存任意长度字符串的内建内存管理机制,支持Unicode,printf风格的格式化工具,等等。因为这种字符串使用的非常频繁,Objective-C提供了一个助记符可以方便地从常量值创建NSString对象。要使用这个助记符,你需要做的全部事情,是在普通的双引号字符串前放置一个@符号。

    NSString* myString = @"my string";

    NSString* anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];

    类是 Objective-C 用来封装数据,以及操作数据的行为的基础结构。对象就是类的运行期间实例,它包含了类声明的实例变量自己的内存拷贝,以及类成员的指针。Objective-C 的类规格说明包含了两个部分:定义(interface)与实现(implementation)。定义(interface)部分包含了类声明和实例变量的定义,以及类相关的方法。实现(implementation)部分包含了类方法的实际代码。

    类声明总是由 @interface 编译选项开始,由 @end 编译选项结束。类名之后的(用冒号分隔的)是父类的名字。类的实例(或者成员)变量声明在被大括号包含的代码块中。实例变量块后面就是类声明的方法的列表。每个实例变量和方法声明都以分号结尾。

    类声明图

    Interface

    定义部分,清楚定义了类的名称、数据成员和方法。 以关键字@interface作为开始,@end作为结束。

    @interface MyObject : NSObject {
        int memberVar1; // 实体变量
        id  memberVar2;
    }
    
    +(return_type) class_method; // 类方法
    
    -(return_type) instance_method1; // 实例方法
    -(return_type) instance_method2: (int) p1;
    -(return_type) instance_method3: (int) p1 andPar: (int) p2;
    @end

    方法前面的 +/- 号代表函数的类型:加号(+)代表类方法class method),不需要实例就可以调用,与C++ 的静态函数(static member function)相似。减号(-)即是一般的实例方法instance method)。

    Objective-C定义一个新的方法时,名称内的冒号(:)代表参数传递,不同于C语言以数学函数的括号来传递参数。

    - (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法*/
    
    [myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫方法*/

    Interface

    实现区块则包含了公开方法的实现,以及定义私有(private)变量及方法。 以关键字@implementation作为区块起头,@end结尾。

    @implementation MyObject {
      int memberVar3; //私有實體變數
    }
    
    +(return_type) class_method {
        .... //method implementation
    }
    -(return_type) instance_method1 {
         ....
    }
    -(return_type) instance_method2: (int) p1 {
        ....
    }
    -(return_type) instance_method3: (int) p1 andPar: (int) p2 {
        ....
    }
    @end

    值得一提的是不只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方法,来添加额外的工作。(用途类似C++ 的构造函数constructor)

    方法

    方法声明包括方法类型标识符,返回值类型,一个或多个方法标识关键字,参数类型和名信息。下图展示 insertObject:atIndex: 实例方法的声明。声明由一个减号(-)开始,这表明这是一个实例方法。方法实际的名字(insertObject:atIndex:)是所有方法标识关键的级联,包含了冒号。冒号表明了参数的出现。如果方法没有参数,你可以省略第一个(也是唯一的)方法标识关键字后面的冒号。本例中,这个方法有两个参数。

    方法声明语法

    当你想调用一个方法,你传递消息到对应的对象。这里消息就是方法标识符,以及传递给方法的参数信息。发送给对象的所有消息都会动态分发,这样有利于实现Objective-C类的多态行为。也就是说,如果子类定义了跟父类的具有相同标识符的方法,那么子类首先收到消息,然后可以有选择的把消息转发(也可以不转发)给他的父类。

      消息被中括号( [ 和 ] )包括。中括号中间,接收消息的对象在左边,消息(包括消息需要的任何参数)在右边。例如,给myArray变量传递消息insertObject:atIndex:消息,你需要使用如下的语法:

    [myArray insertObject:anObj atIndex:0];

    为了避免声明过多的本地变量保存临时结果,Objective-C允许你使用嵌套消息。每个嵌套消息的返回值可以作为其他消息的参数或者目标。例如,你可以用任何获取这种值的消息来代替前面例子里面的任何变量。所以,如果你有另外一个对象叫做myAppObject拥有方法,可以访问数组对象,以及插入对象到一个数组,你可以把前面的例子写成如下的样子:

    [[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

    与实例方法使用减号作为方法类型标识符不同,类方法使用加号( + )。

    下面的例子演示了一个类方法如何作为类的工厂方法。在这里,arrayWithCapacity是NSMutableArray类的类方法,为类的新实例分配内容并初始化,然后返回给你。

    NSMutableArray*   myArray = nil; // nil 基本上等同于 NULL
    
    // 创建一个新的数组,并把它赋值给 myArray 变量
    myArray = [NSMutableArray arrayWithCapacity:0];

    属性

    属性是用来代替声明存取方法的便捷方式。属性不会在你的类声明中创建一个新的实例变量。他们仅仅是定义方法访问已有的实例变量的速记方式而已。暴露实例变量的类,可以使用属性记号代替getter和setter语法。类还可以使用属性暴露一些“虚拟”的实例变量,他们是部分数据动态计算的结果,而不是确实保存在实例变量内的。

    实际上可以说,属性节约了你必须要写的大量多余的代码。因为大多数存取方法都是用类似的方式实现的,属性避免了为类暴露的每个实例变量提供不同的getter和setter的需求。取而代之的是,你用属性声明指定你希望的行为,然后在编译期间合成基于声明的实际的getter和setter方法。

    属性声明应该放在类接口的方法声明那里。基本的定义使用@property编译选项,紧跟着类型信息和属性的名字。你还可以用定制选项对属性进行配置,这决定了存取方法的行为。下面的例子展示了一些简单的属性声明:

    @interface Person : NSObject {
        @public
            NSString *name;
        @private
            int age;
    }
    
    @property(copy) NSString *name;
    @property(readonly) int age;
    
    -(id)initWithAge:(int)age;
    @end
    

    属性的访问方法由@synthesize关键字来实现,它由属性的声明自动的产生一对访问方法。另外,也可以选择使用@dynamic关键字表明访问方法会由程序员手工提供。

    @implementation Person
    @synthesize name;
    @dynamic age;
    
    -(id)initWithAge:(int)initAge
    {
        age = initAge; // 注意:直接赋给成员变量,而非属性
        return self;
    }
    
    -(int)age
    {
        return 29; // 注意:并非返回真正的年龄
    }
    @end
    

    属性可以利用传统的消息表达式、点表达式或"valueForKey:"/"setValue:forKey:"方法对来访问。

    Person *aPerson = [[Person alloc] initWithAge: 53];
    aPerson.name = @"Steve"; // 注意:点表达式,等于[aPerson setName: @"Steve"];
    NSLog(@"Access by message (%@), dot notation(%@), property name(%@) and direct instance variable access (%@)",
          [aPerson name], aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);
    

    为了利用点表达式来访问实例的属性,需要使用"self"关键字:

    -(void) introduceMyselfWithProperties:(BOOL)useGetter
    {
        NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name)); // NOTE: getter vs. ivar access
    }

    类或协议的属性可以被动态的读取。

    int i;
    int propertyCount = 0;
    objc_property_t *propertyList = class_copyPropertyList([aPerson class], &propertyCount);
    
    for ( i=0; i < propertyCount; i++ ) {
        objc_property_t *thisProperty = propertyList + i;
        const char* propertyName = property_getName(*thisProperty);
        NSLog(@"Person has a property: '%s'", propertyName);
    }

    快速枚举

    比起利用NSEnumerator对象或在集合中依次枚举,Objective-C 2.0提供了快速枚举的语法。在Objective-C 2.0中,以下循环的功能是相等的,但性能特性不同。

    // 使用NSEnumerator
    NSEnumerator *enumerator = [thePeople objectEnumerator];
    Person *p;
    
    while ( (p = [enumerator nextObject]) != nil ) {
        NSLog(@"%@ is %i years old.", [p name], [p age]);
    }
    // 使用依次枚举
    for ( int i = 0; i < [thePeople count]; i++ ) {
        Person *p = [thePeople objectAtIndex:i];
        NSLog(@"%@ is %i years old.", [p name], [p age]);
    }
    
    // 使用快速枚举
    for (Person *p in thePeople) {
        NSLog(@"%@ is %i years old.", [p name], [p age]);
    }
    

    快速枚举可以比标准枚举产生更有效的代码,由于枚举所调用的方法被使用NSFastEnumeration协议提供的指针算术运算所代替了。

    协议(Protocol)

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

    @protocol Locking
    - (void)lock;
    - (void)unlock;
    @end

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

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

    @interface SomeClass : SomeSuperClass <Locking>
    @end

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

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

    由于SomeClass已经确实遵从了Locking协议,故调用端可以安全的发送lock或unlock消息给SomeClass实体变量,不需担心他没有办法回应消息。

    插件是另一个使用抽象定义的例子,可以在不关心插件的实现的情况下定义其希望的行为。

    动态类型

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










    
    
  • 相关阅读:
    Http协议(一)基础知识
    Redis问题汇总
    Sql Server存储过程传递XML参数
    C# Redis
    Task的异常捕获和处理方法
    Entity Framework教程
    WebBrowser 弹窗体关闭原理
    C# 虹软SDK视频人脸识别和注册
    C#性能优化:延迟初始化Lazy
    DZ 特殊主题简单开发教程
  • 原文地址:https://www.cnblogs.com/guors/p/12653380.html
Copyright © 2011-2022 走看看