Objective-C入门(一)

版权声明:如果觉得文章对你有用,转载不需要联系作者,但请注明出处 https://blog.csdn.net/jinxin70/article/details/83548196

Objective-C与C语言的关系

Objective-C是C语言的严格超集,完全兼容C,C语言编写的程序可以不经修改直接被Objective-C编译。

Objective-C的文件扩展名

扩展名 内容类型
.h 头文件。可以包含类,类型,函数和常数的声明
.m 源文件。可以同时包含Objective-C和C代码
.mm 源文件。可以同时包含Objective-C、C代码、C++代码

用来包含头文件的编译选项#include,在OC中则建议使用#import,和#include的区别是,#import可以确保相同的文件只会被包含一次。

方法调用格式:

[obj method:argument]

逐个解释:

obj是类的实现对象;

method是要调用的方法;

argument是方法参数;

方法调用必须用[]括起来。

消息传递

OC语言,消息传递模型,对象之间互相传递消息。

C++类别和方法严格清楚,一个方法必定属于一个类别,编译时已经绑定关系,不可能调用一个不存在类里面的方法。

OC类别和消息(方法)的关系松散,调用方法视为对对象发送消息,所有方法都被视为对消息的回应。所有消息处理直到运行时才会动态绑定,并交由类别自行决定如何处理收到的消息。一个类别不保证一定会回应收到的消息,如果类别收到了一个无法处理的消息,程序只会抛出异常,不会出错或崩溃。

[car fly];

发一个fly的信息给car,fly是消息,而car是消息的接收者。car收到消息后会决定如何回应这个消息,若car类别内定义有fly方法就运行方法内之代码,若car内不存在fly方法,则程序依旧可以通过编译,运行期则抛出异常。

字符串

OC语言中使用一个字符串类型来接受字符串,NSString

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

// 从一个C语言字符串创建Objective-C字符串
NSString*  fromCString = [NSString stringWithCString:"A C string" 
encoding:NSASCIIStringEncoding];

下图展现了声明一个叫做 MyClass 的类的语法,这个类继承自 NSObject 基础类。类声明总是由 @interface 编译选项开始,由 @end 编译选项结束。类名之后的(用冒号分隔的)是父类的名字。类的实例(或者成员)变量声明在被大括号包含的代码块中。实例变量块后面就是类声明的方法的列表。每个实例变量和方法声明都以分号结尾。

类的定义文件遵循C语言之惯例以.h为后缀,实现文件以.m为后缀。

图片来自www.runoob.com

Interface

@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语言以数学函数的括号来传递参数。Objective-C方法使得参数可以夹杂于名称中间,不必全部附缀于方法名称的尾端,可以提高程序可读性(感觉很不习惯( ⊙ o ⊙ )啊!)。设定颜色RGB值的方法为例:

- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法*/

[myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫方法*/

这个方法的签名是setColorToRed:Green:Blue:。每个冒号后面都带着一个float类别的参数,分别代表红,绿,蓝三色。

Implementation

@Implementation用于定义类的实现,以@end结尾。

@Implementation内定义的变量是私有的。

创建对象

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

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

MyObject * my = [MyObject new];

alloc分配内存,init是初始化对象。

方法

方法声明语法

类方法使用+

实例方法使用-

给myArray变量传递消息insertObject:atIndex:消息

[myArray insertObject:anObj atIndex:0];

一个嵌套的消息

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

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

NSMutableArray*   myArray = nil; // nil 基本上等同于 NULL

// 创建一个新的数组,并把它赋值给 myArray 变量
myArray = [NSMutableArray arrayWithCapacity:0];

属性

属性声明应该放在类接口的方法声明那里。基本的定义使用@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协议提供的指针算术运算所代替了。

协议

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

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

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

语法

协议以关键字@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实体变量,不需担心他没有办法回应消息。

动态类型

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"的实例。

转发

Objective-C允许对一个对象发送消息,不管它是否能够响应之。除了响应或丢弃消息以外,对象也可以将消息转发到可以响应该消息的对象。转发可以用于简化特定的设计模式,例如观察者模式或代理模式。

Objective-C运行时在Object中定义了一对方法:

转发方法

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

响应方法

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

类别(Category)

 

文章参考

https://www.runoob.com/w3cnote/objective-c-tutorial.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页