zoukankan      html  css  js  c++  java
  • (译)Learn Objective-C

    原文链接:http://cocoadevcentral.com/d/learn_objectivec/

    开始自学IOS开发,找了一篇比较经典的Objective-C微教程,只是读一遍终觉印象不深,索性尝试翻译一下

    再附上CSDN上的翻译:http://mobile.51cto.com/hot-232833.htm

    Learn Objective-C

    1,Calling Methods

    让我们从简单的示例开始,调用一个对象的方法,基本语法是这样的:

    [object method];
    [object methodWithInput:input];

    对象的方法可以返回值:

    output = [object methodWithOutput];
    output = [object methodWithInputAndOutput:input];

    你也可以调用类中的如何创建对象的方法,在下面的例子中,我们调用NSString类的string方法,从而返回一个新的NSString对象.

    id myObject = [NSString string];

    id类型意味着myObject变量可以被赋予任意类型的对象,所以在你编译app时,并不知道它真实实现的类和方法.

    在下面的例子里,它很明显将是一个NSString,所以我们在前面指定它的类型:

    NSString* myString = [NSstring string];

    这是一个NSString变量,所以如果我们尝试在这个对象上使用一个NSString不支持的方法,编译器就会发出警告.

    注意类类型的右边有个*号,所有的Objective-C对象变量都是指针类型.id类型是预定义的指针类型,所以不需要加*号.

    Nested Messages

    在许多语言中,嵌套调用方法和函数是这样的写法: 

    function1( function2() );

    function2的返回值作为function1的传入参数.在Objective-C中,嵌套信息这样写:

    [NSString stringWithFormat:[prefs format]];

    应避免在同一行里调用超过两个嵌套信息,这样会导致可读性变差.

    Mulit-Input Methods

    通常我们会需要一个方法拥有多个参数.在Objective-C中,一个方法名能被分成N段.在头文件里,一个多参方法定义如下:

    -(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;

    调用方法:

    BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];

    这里不仅仅是命名参数.这个方法在系统运行中的名字实际是writeToFile:atomically:

    2,Accessors

    所有的实例变量在Objective-C中默认是私有的,所以在多数情况下你需要用Accessors 来 获取/设置 变量的值.
    有两个语法,下面是传统的1.X语法:

    [photo setCaption:@"Day at the Beach"];
    output = [photo caption];

    第二行代码不是直接读取实例变量.实际上它是调用了caption方法.在多数情况下,Objective-C中的getters不加前缀get.

    无论何时你只要看到方括号里的代码,这就代表给类或对象发送消息.

    Dot Syntax

    Dot Syntax是Objective-C 2.0中getters和setters的新方法,这也是Mac OS X 10.5的一部分:

    photo.caption = @"Day at the Beach";
    output = photo.caption;

    这两种方式你都可以使用,但在一个工程最好选择统一的风格,

    注意:Dot Syntax只能用在setters和getters,不能用于其它用途的方法.



    3,Creating Objects


    有两个创建类的方法,第一种我们之前已经写过:

    NSString* myString = [NSString string];

    这是更加便捷自动的方式,在这个例子中,你创建了一个autoreleased类型的类,关于autorelease后面我们会涉及到更多细节.

    在多数情况下,我们需要用手动的方式来创建类:

    NSString* myString = [[NSString alloc] init];

    这是一个嵌套方法的调用.首先调用了NSString自己的alloc方法.请求驻留内存和实例化一个对象,这些属于相当底层的调用.

    而后面则是在新创建的对象上调用init方法.init实现通常用作一些基本设置,比如创建实例变量.
    我们并不知道它的具体细节,它是作为一个客户端的类实现的.

    有时候,你要用到需要赋值的init

    NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];

     

    4,Basic Memory Management


    如果在Max OS X上写应用,你可以选择启用垃圾回收的功能.一般来说,这意味着除非遇到复杂的情况,你不需要考虑内存管理的问题.
    尽管如此,你不会总在一个支持垃圾回收的环境上工作.所以你需要稍微知道一些基本的内存管理概念.

    如果你通过手工alloc的方式创建了一个对象,你需要在后面release掉这个对象.
    你不应该手动release一个autoreleased类型的对象,因为如果你这样做,程序就会崩溃.

    这里有两个例子:

    //string1 will be released automatically
    NSString* string1 = [NSString astring];
    
    //must release this when done
    NSString* string2 = [[NSString alloc] init];
    [string2 release];

    在这个示例中,你可以假设自动释放的对象(string1)会在当前函数调用结束后销毁.

    关于内存管理还有更多的学习,但我们会在后面了解更多的概念后再了解它.

     

    5,Designing a Class Interface

    Objective-C创建类的语句非常简单,通常含有两个部分:

    类的接口一般保存在ClassName.h文件中,定义了实例变量和公共方法.

    实现部分则在ClassName.m文件中,这里面包含了具体实现方法的代码,同样也会经常定义一些子类不可见的private方法.

    这里有个接口文件,类名叫Photo,所以文件名是Photo.h:

    #import <Cocoa/Cocoa.h>
    
    @interface Photo: NSObject {
    NSString* caption;
    NSString* photographer;
    }
    @end

    首先,我们导入Cocoa.h,这个是Cocoa程序所有基本的类.#import指令会自动避免在同一个文件中指定多次.

    @interface是指Photo类的一个声明,冒号后面跟着NSObject这个超类.
    在大括号里面,有两个实体变量:caption和photographer.都是NSStrings类型,也可以是任意对象类型,包括id类型.
    最后,@end结束类的声明.

     

    Add Methods

    让我们对实体变量加一些getters

    #import <Cocoa/Cocoa.h>
    
    @interface Photo: NSObject {
    NSString* caption;
    NSString* photographer;
    }
    
    - caption;
    - photographer;
    @end

    记住,Objective-C的特点是省略get前缀.

    方法名前面加 - 表示是实例方法
    方法名前面加 + 表示是类方法.

    默认情况下,编译器会假设方法返回id类型的对象,所有传入参数也默认是id类型.
    所以上面的代码技术上是没有问题的,但通常不会这样做.
    让我们给返回值添加上指定的类型:

    #import <Cocoa/Cocoa.h>
    
    @interface Photo: NSObject {
    NSString* caption;
    NSString* photographer;
    }
    
    - (NSString*) caption;
    - (NSString*) photographer;
    @end

    现在再添加setters:

    #import <Cocoa/Cocoa.h>
    
    @interface Photo: NSObject {
    NSString* caption;
    NSString* photographer;
    }
    
    - (NSString*) caption;
    - (NSString*) photographer;
    
    - (void) setCaption: (NSString*)input;
    - (void) setPhotographer: (NSString*)input;
    @end

    Setters是不需要返回值的,所以我们指定类型为void

    6, Class Implementation

    然后我们开始创建实现部分,从getters开始:

    #import "Photo.h"
    
    @implementation Photo
    
    - (NSString*) caption {
    return caption;
    }
    -(NSString*) photographer {
    return photographer;
    }
    
    @end

    实现部分开始于 @implementation 和 类名,最后@end.看起来有点像类的接口声明部分.

    在接口声明的方法,同时要在实现部分实现.所有的方法都是如此.

    如果你有过编程基础,会对getters看起来非常熟悉,所以我们转移重点,更多的来解释说明setters:

    - (void) setCaption: (NSString*)input
    {
    [caption autorelease];
    caption = [input retain];
    }
    
    - (void) setPhotographer: (NSString*)input
    {
    [photographer autorealease];
    photographer = [input retain];
    }

    每个setters都分配了两个变量,第一个是已经存在的对象的引用,第二个是新传入的对象.在垃圾回收环境中,

    我们可以只直接设置新对象:

    - (void) setCaption: (NSString*)input
    {
    caption = input;
    }

    但如果你没有用到垃圾回收,你就需要release旧对象,然后retain新对象.

    实际中,有两种方式释放一个对象的引用:release和autorelease.release会立即移除引用.autorelease则会在之后的某个时间进行release,在此之前,对象会一直驻留在内存中直到函数结束.(除非添加自定义代码来处理).

    atuorelease包含在setters里面更安全,因为变量的新值和旧值不能指向同一个对象.你不会立即释放你想要保留的那个对象.
    这样听起来有点迷惑,没关系,后面随着进度你会理解的更加深入,现在你不需要完全明白.

    Init

    我们能创建一个初始化方法,来给我们的实体变量赋一个初始值:

    - (id) init
    {
    if ( self = [super init] )
    {
    [self setCaption:@"Default Caption"];
    [self setPhotographer:@"Default Photographer"];
    }
    return self;
    }

    这个相对来说不需要过多解释,虽然第二行看起来有点不寻常.

    使用单等号将super init的结果分配给self.

    本质上来说,就是请求超类去自我初始化.然后在设置默认值前,用if语句来验证初始化是否成功.

    //Dealloc
    当一个对象开始从内存清除时,将调用dealloc方法.这也是释放所有被引用的子类实体变量的最佳时间:

    - (void) dealloc
    {
    [caption release];
    [photographer release];
    [super dealloc];
    }

    最开始的两行,我们给每个实体变量发送release命令,这里不需要使用autorelease,标准的release更迅速.

    最后一行非常重要,我们必须发送super dealloc信息来请求超类来做自我清除.如果我们没有这样做,对象将不会移除,这样会造成内存泄露.

    当垃圾回收功能启用时,不会调用dealloc方法,声明finalize方法来代替.

    7,More on Memory Management

    Objective-C内存管理系统称作引用计数,你所要必须做的就是保持对引用的跟踪,并在运行中正确的在内存中释放.
    简单来讲:你alloc一个对象,作为一些指针retain,那就需要为每个alloc/retain发送release.所以如果你使用了一次alloc,一次retain,就需要release两次.

    这就是引用计数的理论,在实践中,通常只有两个原因创建对象:
    1,将它作为一个实体变量保留
    2,在函数中单一的临时使用

    在多数情况下,一个实体变量的setters,应该只是autorelease旧对象,并retain新对象.然后一定要dealloc中释放它们.

    所以唯一的真正的工作就是管理函数内的本地引用.一个原则:如果你使用alloc或copy创建了一个对象,就要在功能结束时release或者autorelease它们.如果用其它方式创建的对象,就什么也不要做.

    下面是第一种情况,管理一个实体变量

    - (void) setTotalamount: (NSNumber*)input
    {
    [totalAmount autorelease];
    totalAmount = [input retain];
    }
    
    - (void) dealloc
    {
    [totalAmount release];
    [super dealloc];
    }

    然后另外一种情况,本地引用,我们只需要release用alloc创建的对象:

    NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
    NSNumber* value2 = [[NSNumber numberWithFloat:14.78];
    
    //only release value1 , not value2
    [value1 release];

    再试着理解下面的例子:用一个本地引用给一个实体变量赋值

    NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
    [self setTotal:value1];
    
    NSNumber* value2 = [[NSNumber numberWithFloat:14.78];
    [self setTotal:value2];
    
    [value1 release];

    注意不管你是否将它们赋值给实体变量,管理本地引用的规则都是一致的.你不需要考虑setters是如何实现的

    如果你弄懂这些,你就弄懂了Objective-C内存管理的90%

    8,Logging

    输出日志信息到控制台在Objective-C中非常简单.事实上,NSLog()函数几乎和C的printf()完全相同,除了需要添加%@标示来表示对象.

    NSLog ( @"The current date and time is : %@",[NSDate date] );

    你能记录一个对象到控制台,NSLog函数调用对象的Description方法,并以NSString的形式打印出来.你能在你的类里重载description方法,返回自定义的字符串.

     

    9,Properties

    properties作为OC里的一个功能,允许我们自动产生accessors,另外还有一些别的好处.
    我们让Photo类来使用properties.
    下面是我们之前看过的代码:

    #import <Cocoa/Cocoa.h>
    
    @interface Photo : NSObject {
    NSString* caption;
    NSString* photographer;
    }
    - (NSString*) caption;
    - (NSString*) photographer;
    
    - (void) setCaption: (NSString*)input;
    - (void) setPhotographer: (NSString*)input;
    
    @end

    一旦用属性来实现便如下:

    #import <cocoa/cocoa.h>
    
    @interface Photo : NSObject {
    NSString* caption;
    NSString* photographer;
    }
    @property (retain) NSString* caption;
    @property (retain) NSString* photographer;
    
    @end

    @property在OC里是声明属性的指令.圆括号里的retain指定setter保留输入值,接下来的部分指定属性的名称和类型.

    现在我们看一下类的实现部分:

    #import "Photo.h"
    
    @implementation Photo
    
    @synthesize caption;
    @synthesize photographer;
    
    - (void) dealloc
    {
    [caption release];
    [photographer release];
    [super dealloc];
    }
    
    @end

    @synthesize指令会自动生成setters和getters,所以我们只需要在类里实现dealloc方法就可以了.

    Accessors只会在setters和getters不存在的时候自动创建它们,所以随意的用@synthesize来指定属性,如果需要可以在随后的实现部分自定义setter或getter.编译器会检查哪个方法没有,自动填充漏掉的方法.

    还有好多关于属性声明的选项,不过那些都在本教程范围之外了.

    10,Calling Methods on Nil

    在OC中,nil对象类似于其它语言的null.不同的是你可以调用nil方法,而不会引起程序崩溃或抛出一个异常.

    这个技巧在框架中有许多不同方面的使用方式,但目前对你来说最主要的是当你调用一个方法时不必检查它是否为nil.
    如果你调用了一个nil的方法来返回一个对象,就会得到一个nil.

    我们可以利用这个特性稍微改善下dealloc方法:

    - (void) dealloc
    {
    self.caption = nil;
    self.photographer = nil;
    [super dealloc];
    }

    我们把nil赋给一个实体变量,setter只会保留nil(没有做任何事情)和release旧的值.

    这样做的好处是对象的变量指针不会指向到其它一些随机数据上.

    注意我们在这里使用了 self.<var>语法,意思是使用setter并使用内存管理释放.如果直接设置nil,会引起内存泄露.

    //incorrect. causes a memory leak.
    // use self.caption to go through setter
    caption = nil;

    11,Categories

    Categories是OC中最有用的功能之一.本质上,一个category允许你在一个已存在的类上添加方法,而不必用子类继承,也不需要知道类内部具体实现的细节.

    这个特别有用,你能在内嵌的对象上添加方法.如果你想在你的应用中所有的NSString实例添加一个方法,你只需要添加一个category.而不必每个都使用定制子类.

    举个例子,如果我想在NSString上添加一个方法,确定其内容是否是一个URL:

    #import <cocoa/cocoa.h>
    
    @interface NSString (Utilities)
    - (BOOL) isURL;
    @end

    这个和类声明非常相似,不同的是这里没有列出超类(super class),括号里的是category的名字,这个名字可以任意取,通过名字和内部的方法沟通.

    下面是实现部分,记住这个不是一个好的检测URL的实现,我们只是通过这个来简单诠释category的概念:

    #import "NSString-Utilities.h"
    
    @implementation NSString (Utilities)
    
    - (BOOL) isURL
    {
    if ( [self hasPreFix:@"http://"] )
    return YES;
    else
    return NO;
    }
    
    @end

    接下来你就能在所有的NNString上使用这个方法.下面的代码将会在命令行中打印出"string1 is a URL":

    NSString* string1 = @"http://pixar.com/";
    NSString* string2 = @"Pixar";
    
    if ( [string1 isURL] )
    NSLog (@"string1 is a URL");
    
    if ( [string2 isURL] )
    NSLog (@"string2 is a URL");

    不像子类,categories不能添加到实体变量上.虽然能通过categories来重载类中已存在的方法,但这样做必须非常小心.

    记住,当你通过category来改变一个类时,它会影响整个应用程序中该类的所有对象.

     

     

  • 相关阅读:
    刷脸背后:人脸检测人脸识别人脸检索_张重生资料整理
    webpack工具
    js精度缺失和最大安全整数
    在线文档预览(干货篇)
    讨论js比较两个数组(对象)是否相等的范围
    js不同数据类型中==与===的对比
    js中this的指向
    前后端数据类型
    js网页节点操作
    圆角渐变边框实现
  • 原文地址:https://www.cnblogs.com/alphaqcode/p/3910101.html
Copyright © 2011-2022 走看看