OC温习


OC温习2023.5.15

========================
Objective-C温习一下
xc

1、Objective-C数据类型

据我所收集的资料,总结如下:

char
1字节
-128 ~ 127 或 0 ~ 255


unsigned char
1字节
0 ~ 255


signed char
1字节
-128 ~ 127


int
2或4字节
-32,768 ~ 32,767 或 -2,147,483,648 ~ 2,147,483,647


unsigned int
2或4字节
0 ~ 65,535 或 0 ~ 4,294,967,295


short
2字节
-32,768 ~ 32,767


unsigned short
2字节
0 ~ 65,535


long
4字节
-2,147,483,648 ~ 2,147,483,647


unsigned long
4字节
0 ~ 4,294,967,295

float
4字节
1.2E-38 ~ 3.4E+38
6位小数


double
8字节
2.3E-308 ~ 1.7E+308
15位小数


long double
10字节
3.4E-4932 ~ 1.1E+4932
19位小数

函数指定返回void
Objective-C中有各种函数,它们不需要返回值,或者也可以说它们返回void。 没有返回值的函数的返回类型为void。 例如,void exit(int status);

函数参数为void
Objective-C中有各种函数不接受任何参数。没有参数的函数可以指示接受void类型。 例如,int rand(void);
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_data_types.html

```txt
### 2、Objective-C块的使用
与Swift中的闭包相似
```txt
Objective-C类定义了一个将数据与相关行为相结合的对象。 有时,仅表示单个任务或行为单元而不是方法集合是有意义的。
块是C,Objective-C和C++等编程语言中的高级功能,它允许创建不同的代码段,这些代码段可以传递给方法或函数,就像它们是值一样。 块是Objective-C对象,因此它们可以添加到NSArray或NSDictionary等集合中。 它们还能够从封闭范围中捕获值,使其类似于其他编程语言中的闭包或lambda。
简单块声明语法
returntype (^blockName)(argumentType);
Objective-C
简单的块实现 - 
returntype (^blockName)(argumentType)= ^{
};
Objective-C
下面是一个简单的示例代码 - 
void (^simpleBlock)(void) = ^{
   NSLog(@"This is a block");
};
Objective-C
调用上面块的示例代码 - 
simpleBlock();
Shell
块接受参数和返回值块也可以像方法和函数一样获取参数和返回值。下面是一个使用参数和返回值实现和调用块的简单示例。
double (^multiplyTwoValues)(double, double) = 
   ^(double firstValue, double secondValue) {
      return firstValue * secondValue;
   };

double result = multiplyTwoValues(2,4); 
NSLog(@"The result is %f", result);
Objective-C
使用类型定义块这是一个在块中使用typedef的简单示例。 请注意,此示例不适用于在线编译器。 它是使用XCode运行的。
#import <Foundation/Foundation.h>

typedef void (^CompletionBlock)();
@interface SampleClass:NSObject
- (void)performActionWithCompletion:(CompletionBlock)completionBlock;
@end

@implementation SampleClass

- (void)performActionWithCompletion:(CompletionBlock)completionBlock {

   NSLog(@"Action Performed");
   completionBlock();
}

@end

int main() {

   /* 第一个Objective-C程序 */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass performActionWithCompletion:^{
      NSLog(@"Completion is called to intimate action is performed.");
   }];

   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-10 08:14:57.105 demo[184:323] Action Performed
2018-11-10 08:14:57.108 demo[184:323] Completion is called to intimate action is performed.
Shell
块在iOS应用程序和Mac OS X中使用得更多。因此,了解块的用法更为重要。
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_blocks.html

```txt
### 3、Objective-C预处理器
```txt
Objective-C预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。 简单来说,Objective-C预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。 我们将Objective-C预处理器称为OCPP。
所有预处理器命令都以井号(#)开头。它必须是第一个字符(前面不能有空格),并且为了便于阅读,预处理器指令应该从第一列开始。 以下部分列出了所有重要的预处理程序指令 -



编号
指令
描述




1
#define
替换预处理器宏


2
#include
从另一个文件插入特定标头


3
#undef
未定义的预处理器宏


4
#ifdef
如果定义了此宏,则返回true


5
#ifndef
如果未定义此宏,则返回true


6
#if
测试编译时条件是否为true


7
#else
#if的替代方案


8
#elif
在#else和 #if 中的一个语句


9
#endif
结束预处理器条件


10
#error
在stderr上打印错误消息


11
#pragma
使用标准化方法向编译器发出特殊命令



1. 预处理器示例分析以下示例以了解各种宏的指令。
#define MAX_ARRAY_LENGTH 20
C
该指令告诉OCPP 用20替换MAX_ARRAY_LENGTH宏标识。使用#define定义常量来提高可读性。
#import <Foundation/Foundation.h>
#include "myheader.h"
Objective-C
这些指令告诉OCPP 从Foundation Framework获取foundation.h,并将文本添加到当前源文件中。 下一行告诉OCPP 从本地目录获取myheader.h并将内容添加到当前源文件。
#undef  FILE_SIZE
#define FILE_SIZE 42
C
它告诉OCPP 取消定义现有的FILE_SIZE,并将FILE_SIZE定义为42。
#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif
C
它告诉OCPP仅在尚未定义MESSAGE宏时才定义MESSAGE。
#ifdef DEBUG
   /* Your debugging statements here */
#endif
C
它告诉OCPP如果定义了DEBUG,则执行包含语句的过程。 如果在编译时将- DDEBUG标志传递给gcc编译器,这将非常有用。 它将定义DEBUG,因此可以在编译期间动态打开和关闭调试。
2. 预定义的宏ANSI C定义了许多宏。虽然每个都可用于编程,但不应直接修改预定义的宏。



编号
宏
描述




1
__DATE__
当前日期为“MMM DD YYYY”格式的字符文字


2
__TIME__
当前时间作为“HH:MM:SS”格式的字符文字


3
__FILE__
它包含当前文件名作为字符串文字。


4
__LINE__
它包含当前行号作为十进制常量。


5
__STDC__
当编译器符合ANSI标准时,定义为1。



试试下面的例子代码 -
#import <Foundation/Foundation.h>

int main() {
   NSLog(@"File :%s\n", __FILE__ );
   NSLog(@"Date :%s\n", __DATE__ );
   NSLog(@"Time :%s\n", __TIME__ );
   NSLog(@"Line :%d\n", __LINE__ );
   NSLog(@"ANSI :%d\n", __STDC__ );

   return 0;
}
Objective-C
当编译并执行文件main.m 中的上述代码时,它会产生以下结果 -
2018-11-15 08:44:54.041 main[50640] File :main.m
2018-11-15 08:44:54.042 main[50640] Date :Nov 15 2018
2018-11-15 08:44:54.042 main[50640] Time :08:44:52
2018-11-15 08:44:54.042 main[50640] Line :7
2018-11-15 08:44:54.043 main[50640] ANSI :1
Shell
3. 预处理器运算符Objective-C预处理器提供以下运算符来创建宏 -
3.1. 宏延续(\)宏通常必须包含在一行中。宏延续运算符用于继续对于单行来说太长的宏。 例如 -
#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")
Objective-C
3.2. 字符串化(#)字符串化或数字符号运算符(#)在宏定义中使用时,将宏参数转换为字符串常量。 此运算符只能在具有指定参数或参数列表的宏中使用。 例如 -
#import <Foundation/Foundation.h>

#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")

int main(void) {
   message_for(Carole, Debra);
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-15 08:56:38.088 main[98681] Carole and Debra: We love you!
Shell
3.3. 令牌粘贴(##)宏定义中的令牌粘贴运算符(##)组合了两个参数。 它允许将宏定义中的两个单独的标记连接到一个标记中。 例如 -
#import <Foundation/Foundation.h>

#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)

int main(void) {
   int token34 = 40;

   tokenpaster(34);
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-15 08:58:04.872 main[138839] token34 = 40
Shell
它是如何发生的,因为这个例子导致预处理器的以下实际输出 -
NSLog (@"token34 = %d", token34);
Shell
此示例显示令牌##n到token34的串联,这里使用了stringize和token-pasting。
3.4. defined()运算符预处理器定义的运算符用于常量表达式,以确定是否使用#define定义标识符。如果定义了指定的标识符,则该值为true(非零)。 如果未定义符号,则值为false(零)。 定义的运算符指定如下 -
#import <Foundation/Foundation.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void) {
   NSLog(@"Here is the message: %s\n", MESSAGE);  
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-15 09:04:30.790 main[31654] Here is the message: You wish!
Shell
4. 参数化宏OCPP的一个强大功能是使用参数化宏模拟函数的能力。 例如,可能需要一些代码来对数字进行平方,如下所示 -
int square(int x) {
   return x * x;
}
Objective-C
可以使用宏重写上面的代码,如下所示 -
#define square(x) ((x) * (x))
Objective-C
必须先使用#define指令定义带参数的宏,然后才能使用它们。 参数列表括在括号中,并且必须紧跟宏名称。 宏名称和左括号之间不允许有空格。 例如 -
#import <Foundation/Foundation.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
   NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));  
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_preprocessors.html

4、iOS中NSError的使用

Objective-C程序使用NSError对象来传达有关用户需要了解的运行时错误的信息。 在大多数情况下,程序会在对话框或工作表中显示此错误信息。 但它也可能会解释信息并要求用户尝试从错误中恢复或尝试自行更正错误
NSError对象包括 -

域 - 错误域可以是预定义的NSError域之一,也可以是描述自定义域和域的任意字符串,不能为nil。代码 - 错误的错误代码。用户信息 - 错误和userInfo的字典可能为nil。
以下示例代码显示如何创建自定义错误 - 
NSString *domain = @"com.yiibai.MyApplication.ErrorDomain";
NSString *desc = NSLocalizedString(@"Unable to complete the process", @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
Objective-C
以下是作为对指针的引用传递的上述错误示例的完整代码 -
#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
-(NSString *) getEmployeeNameForID:(int) id withError:(NSError **)errorPtr;
@end

@implementation SampleClass

-(NSString *) getEmployeeNameForID:(int) id withError:(NSError **)errorPtr {
   if(id == 1) {
      return @"Employee Test Name";
   } else {
      NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
      NSString *desc =@"Unable to complete the process";
      NSDictionary *userInfo = [[NSDictionary alloc] 
      initWithObjectsAndKeys:desc,
      @"NSLocalizedDescriptionKey",NULL];  
      *errorPtr = [NSError errorWithDomain:domain code:-101 
      userInfo:userInfo];
      return @"";
   }
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   SampleClass *sampleClass = [[SampleClass alloc]init];
   NSError *error = nil;
   NSString *name1 = [sampleClass getEmployeeNameForID:1 withError:&error];

   if(error) {
      NSLog(@"Error finding Name1: %@",error);
   } else {
      NSLog(@"Name1: %@",name1);
   }

   error = nil;
   NSString *name2 = [sampleClass getEmployeeNameForID:2 withError:&error];

   if(error) {
      NSLog(@"Error finding Name2: %@",error);
   } else {
      NSLog(@"Name2: %@",name2);
   }

   [pool drain];
   return 0; 
}
Objective-C
在上面的例子中,如果id为1,则返回一个名称,否则设置用户定义的错误对象。
编译并执行上述代码时,会产生以下结果 -
2018-11-15 13:53:52.027 main[136833] Name1: Employee Test Name
2018-11-15 13:53:52.029 main[136833] Error finding Name2: Unable to complete the process
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_error_handling.html

5、Objective-C命令行参数

执行时,可以将一些值从命令行传递给Objective-C程序。 这些值称为命令行参数,很多时候它们对程序很重要,特别是当想要从外部控制程序而不是在代码中对这些值进行硬编码时就很有用了。
命令行参数使用main()函数参数处理,其中argc表示传递的参数数量,argv[]是指针数组,指向传递给程序的每个参数。 以下是一个简单的示例,它检查命令行是否提供了任何参数并采取相应的操作 -
#import <Foundation/Foundation.h>

int main( int argc, char *argv[] ) {
   if( argc == 2 ) {
      NSLog(@"The argument supplied is %s\n", argv[1]);
   } else if( argc > 2 ) {
      NSLog(@"Too many arguments supplied.\n");
   } else {
      NSLog(@"One argument expected.\n");
   }
}
Objective-C
当使用单个参数编译和执行上述代码时,例如使用命令行参数“testing”,它会产生以下结果。
$./main testing
2018-11-15 22:59:51.283 main[79514] The argument supplied is testing
Shell
当使用两个参数(例如testing1和testing2)编译和执行上述代码时,它会产生以下结果。
$./main testing testing2
2018-11-15 22:59:51.283 main[79514] Too many arguments supplied.
Shell
当编译并执行上述代码而不传递任何参数时,它会产生以下结果。
$./main testing testing2
2018-11-15 22:59:51.283 main[79514] One argument expected
Shell
应该注意的是,argv [0] 保存程序本身的名称,argv [1]是指向提供的第一个命令行参数的指针,而* argv [n]是最后一个参数。 如果没有提供参数,argc的值将为1,否则如果传递一个参数,则argc设置为2。
传递由空格分隔的所有命令行参数,但如果参数本身有空格,则可以通过将这些参数放在双引号("")或单引号('')中来传递这些参数。再次重写上面的例子,将打印程序名称,也通过放入双引号传递命令行参数 -
#import <Foundation/Foundation.h>

int main( int argc, char *argv[] ) {
   NSLog(@"Program name %s\n", argv[0]);

   if( argc == 2 ) {
      NSLog(@"The argument supplied is %s\n", argv[1]);
   } else if( argc > 2 ) {
      NSLog(@"Too many arguments supplied.\n");
   } else {
      NSLog(@"One argument expected.\n");
   }

   return 0;
}
Objective-C
当上面的代码被编译并执行时,一个参数用空格分隔,但在双引号中是"Testing1 Testing2",它会产生以下结果。
2018-11-15 23:05:06.113 main[66421] Program name main
2018-11-15 23:05:06.115 main[66421] One argument expected.
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_command_line_arguments.html

6、Objective-C继承与多态

面向对象编程中最重要的概念之一是继承。继承允许根据一个类定义另一个类,这样可以更容易地创建和维护一个应用程序。 这也提供了重用代码功能和快速实现时间的机会。
在创建类时,程序员可以指定新类应该继承现有类的成员,而不是编写全新的数据成员和成员函数。 此现有类称为基类,新类称为派生类。
继承的想法实现了这种关系。 例如,哺乳动物是一个种类的动物,狗是一种哺乳动物,因此狗是一个动物等等。
1. 基础和派生类Objective-C只允许多级继承,即它只能有一个基类但允许多级继承。 Objective-C中的所有类都派生自超类NSObject。
语法如下 - 
@interface derived-class: base-class
Objective-C
考虑一个基类Person及其派生类Employee的继承关系实现如下 -
#import <Foundation/Foundation.h>

@interface Person : NSObject {
   NSString *personName;
   NSInteger personAge;
}

- (id)initWithName:(NSString *)name andAge:(NSInteger)age;
- (void)print;

@end

@implementation Person

- (id)initWithName:(NSString *)name andAge:(NSInteger)age {
   personName = name;
   personAge = age;
   return self;
}

- (void)print {
   NSLog(@"Name: %@", personName);
   NSLog(@"Age: %ld", personAge);
}

@end

@interface Employee : Person {
   NSString *employeeEducation;
}

- (id)initWithName:(NSString *)name andAge:(NSInteger)age 
  andEducation:(NSString *)education;
- (void)print;
@end

@implementation Employee

- (id)initWithName:(NSString *)name andAge:(NSInteger)age 
   andEducation: (NSString *)education {
      personName = name;
      personAge = age;
      employeeEducation = education;
      return self;
   }

- (void)print {
   NSLog(@"姓名: %@", personName);
   NSLog(@"年龄: %ld", personAge);
   NSLog(@"文化: %@", employeeEducation);
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];        
   NSLog(@"基类Person对象");
   Person *person = [[Person alloc]initWithName:@"Maxsu" andAge:25];
   [person print];
   NSLog(@"继承类Employee对象");
   Employee *employee = [[Employee alloc]initWithName:@"Yii bai" 
   andAge:26 andEducation:@"MBA"];
   [employee print];        
   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-16 01:51:21.279 main[138079] 基类Person对象
2018-11-16 01:51:21.280 main[138079] Name: Maxsu
2018-11-16 01:51:21.281 main[138079] Age: 25
2018-11-16 01:51:21.281 main[138079] 继承类Employee对象
2018-11-16 01:51:21.281 main[138079] 姓名: Yii bai
2018-11-16 01:51:21.281 main[138079] 年龄: 26
2018-11-16 01:51:21.281 main[138079] 文化: MBA
Shell
2. 访问控制和继承如果派生类在接口类中定义,则它可以访问其基类的所有私有成员,但它不能访问在实现文件中定义的私有成员。
可以通过以下方式访问它们来执行不同的访问类型。派生类继承所有基类方法和变量,但以下情况除外 -

无法访问在扩展帮助下在实现文件中声明的变量。无法访问在扩展帮助下在实现文件中声明的方法。如果继承的类在基类中实现该方法,则执行继承类中的方法。

                    

                    
                        ( adsbygoogle = window.adsbygoogle || []).push({});
                        
                

                

                

                    
                    

                    多态性这个词表示有许多形式。 通常,当存在类的层次结构并且通过继承相关时,会发生多态性。
Objective-C多态表示对成员函数的调用将导致执行不同的函数,具体取决于调用该函数的对象的类型。
考虑下面一个例子,有一个基类Shape类,它为所有形状提供基本接口。 Square和Rectangle类派生自基Shape类。
下面使用printArea方法来展示OOP特征多态性。
#import <Foundation/Foundation.h>

@interface Shape : NSObject {
   CGFloat area;
}

- (void)printArea;
- (void)calculateArea;
@end

@implementation Shape
- (void)printArea {
   NSLog(@"The area is %f", area);
}

- (void)calculateArea {

}

@end

@interface Square : Shape {
   CGFloat length;
}

- (id)initWithSide:(CGFloat)side;
- (void)calculateArea;

@end

@implementation Square
- (id)initWithSide:(CGFloat)side {
   length = side;
   return self;
}

- (void)calculateArea {
   area = length * length;
}

- (void)printArea {
   NSLog(@"The area of square is %f", area);
}

@end

@interface Rectangle : Shape {
   CGFloat length;
   CGFloat breadth;
}

- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth;
@end

@implementation Rectangle
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth {
   length = rLength;
   breadth = rBreadth;
   return self;
}

- (void)calculateArea {
   area = length * breadth;
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   Shape *square = [[Square alloc]initWithSide:10.0];
   [square calculateArea];
   [square printArea];
   Shape *rect = [[Rectangle alloc]
   initWithLength:10.0 andBreadth:5.0];
   [rect calculateArea];
   [rect printArea];        
   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果 - 
2018-11-16 02:02:22.096 main[159689] The area of square is 100.000000
2018-11-16 02:02:22.098 main[159689] The area is 50.000000
Shell
在上面的示例中,calculateArea和printArea方法的可用性,无论是基类中的方法还是执行派生类。
多态性基于两个类的方法实现来处理基类和派生类之间的方法切换。

7、Objective-C数据封装

所有Objective-C程序都由以下两个基本要素组成 -

程序语句(代码) - 这是执行操作的程序的一部分,它们被称为方法(函数)。程序数据 - 数据是受程序功能影响的程序信息。
封装是一种面向对象的编程概念,它将操作数据的数据和功能绑定在一起,并保护其免受外部干扰和误用。 数据封装导致了重要的OOP数据隐藏概念。
数据封装是捆绑数据和使用函数的机制,数据抽象是一种仅暴露接口并从用户隐藏实现细节的机制。
Objective-C通过创建用户定义类型(称为类)来支持封装和数据隐藏的属性。 例如 -
@interface Adder : NSObject {
   NSInteger total;
}

- (id)initWithInitialNumber:(NSInteger)initialNumber;
- (void)addNumber:(NSInteger)newNumber;
- (NSInteger)getTotal;

@end
Objective-C
变量total是私有的,因此无法从类外部访问。只能由Adder类的其他成员访问它们,而不能由程序的任何其他部分访问。这是实现封装的一种方式。
接口文件中的方法是可访问的,并且在范围内是公共的。
有一些私有方法,这些方法是在扩展的帮助下编写的,我们将在后面的章节中学习。
1. 数据封装示例任何使用公共和私有成员变量实现类的Objective-C程序都是数据封装和数据抽象的一个例子。 考虑以下示例 -
#import <Foundation/Foundation.h>

@interface Adder : NSObject {
   NSInteger total;
}

- (id)initWithInitialNumber:(NSInteger)initialNumber;
- (void)addNumber:(NSInteger)newNumber;
- (NSInteger)getTotal;

@end

@implementation Adder
-(id)initWithInitialNumber:(NSInteger)initialNumber {
   total = initialNumber;
   return self;
}

- (void)addNumber:(NSInteger)newNumber {
   total = total + newNumber;
}

- (NSInteger)getTotal {
   return total;
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];        
   Adder *adder = [[Adder alloc]initWithInitialNumber:10];
   [adder addNumber:15];
   [adder addNumber:14];

   NSLog(@"The total is %ld",[adder getTotal]);
   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果:
2018-11-16 02:19:46.326 main[52227] The total is 39
Shell
上面的类将数字值相加并返回总和。 公共成员addNum和getTotal是外部接口,用户需要知道它们才能使用该类。 私有成员total是对外界隐藏的东西,但是此类需要正常运作。
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_data_encapsulation.html

8、Objective-C协议

Objective-C允许定义协议,声明预期用于特定情况的方法。 协议在符合协议的类中实现。
一个简单的例子是网络URL处理类,它将具有一个协议,其中包含processCompleted委托方法等方法,当网络URL提取操作结束,就会调用类。
协议的语法如下所示 - 
@protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end
Objective-C
关键字@required下的方法必须在符合协议的类中实现,并且@optional关键字下的方法是可选的。
以下是符合协议的类的语法 - 
@interface MyClass : NSObject <MyProtocol>
...
@end
Objective-C
MyClass的任何实例不仅会响应接口中特定声明的方法,而且MyClass还会为MyProtocol中的所需方法提供实现。 没有必要在类接口中重新声明协议方法 - 采用协议就足够了。
如果需要一个类来采用多个协议,则可以将它们指定为以逗号分隔的列表。下面有一个委托对象,它包含实现协议的调用对象的引用。
一个例子如下所示 - 
#import <Foundation/Foundation.h>

@protocol PrintProtocolDelegate
- (void)processCompleted;

@end

@interface PrintClass :NSObject {
   id delegate;
}

- (void) printDetails;
- (void) setDelegate:(id)newDelegate;
@end

@implementation PrintClass
- (void)printDetails {
   NSLog(@"Printing Details");
   [delegate processCompleted];
}

- (void) setDelegate:(id)newDelegate {
   delegate = newDelegate;
}

@end

@interface SampleClass:NSObject<PrintProtocolDelegate>
- (void)startAction;

@end

@implementation SampleClass
- (void)startAction {
   PrintClass *printClass = [[PrintClass alloc]init];
   [printClass setDelegate:self];
   [printClass printDetails];
}

-(void)processCompleted {
   NSLog(@"Printing Process Completed");
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass startAction];
   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果 - 
2018-11-16 03:10:19.639 main[18897] Printing Details
2018-11-16 03:10:19.641 main[18897] Printing Process Completed
Shell
在上面的例子中,已经看到了如何调用和执行委托方法。 它以startAction开始,当进程完成,就会调用委托方法processCompleted以使操作完成。
在任何iOS或Mac应用程序中,如果没有代理,将永远不会实现程序。 因此,要是了解委托的用法。 委托对象应使用unsafe_unretained属性类型以避免内存泄漏。

                    
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_protocols.html

7、Objective-C动态绑定

动态绑定确定在运行时而不是在编译时调用的方法。 动态绑定也称为后期绑定。 在Objective-C中,所有方法都在运行时动态解析。执行的确切代码由方法名称(选择器)和接收对象确定。
动态绑定可实现多态性。例如,考虑一组对象,包括Rectangle和Square。 每个对象都有自己的printArea方法实现。
在下面的代码片段中,表达式[anObject printArea]执行的实际代码是在运行时确定的。 运行时系统使用方法运行的选择器来识别anObject的任何类中的适当方法。
下面来看一下解释动态绑定的简单代码 -
#import <Foundation/Foundation.h>

@interface Square:NSObject {
   float area;
}

- (void)calculateAreaOfSide:(CGFloat)side;
- (void)printArea;
@end

@implementation Square
- (void)calculateAreaOfSide:(CGFloat)side {
   area = side * side;
}

- (void)printArea {
   NSLog(@"The area of square is %f",area);
}

@end

@interface Rectangle:NSObject {
   float area;
}

- (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth;
- (void)printArea;
@end

@implementation  Rectangle

- (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth {
   area = length * breadth;
}

- (void)printArea {
   NSLog(@"The area of Rectangle is %f",area);
}

@end

int main() {
   Square *square = [[Square alloc]init];
   [square calculateAreaOfSide:8.0];

   Rectangle *rectangle = [[Rectangle alloc]init];
   [rectangle calculateAreaOfLength:10.0 andBreadth:20.0];

   NSArray *shapes = [[NSArray alloc]initWithObjects: square, rectangle,nil];
   id object1 = [shapes objectAtIndex:0];
   [object1 printArea];

   id object2 = [shapes objectAtIndex:1];
   [object2 printArea];

   return 0;
}
Objective-C
执行上面示例代码,得到以下结果 - 
2018-11-16 03:16:53.399 main[53860] The area of square is 64.000000
2018-11-16 03:16:53.401 main[53860] The area of Rectangle is 200.000000
Shell
正如在上面的示例中所看到的,printArea方法是在运行时动态选择调用的。 它是动态绑定的一个示例,在处理类似对象时在很多情况下非常有用。
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_dynamic_binding.html

7、Objective-C集合

1. Objective-C集合集合是基本结构。它用于保存和管理其他对象。 集合的主要目的是提供一种有效存储和检索对象的通用方法。
有几种不同类型的集合。 虽然它们都能实现能够容纳其他对象的相同目的,但它们的主要区别在于检索对象的方式。 Objective-C中使用的最常见的集合是 -

NSSetNSArrayNSDictionaryNSMutableSetNSMutableArrayNSMutableDictionary
如果想了解有关这些结构的更多信息,请参阅Foundation框架中的数据存储。
快速枚举语法
for (classType variable in collectionObject ) { 
  statements 
}
Objective-C
以下是快速枚举的示例 - 
#import <Foundation/Foundation.h>

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSArray *array = [[NSArray alloc]
   initWithObjects:@"string1", @"string2",@"string3",@"yii",@"bai",nil];

   for(NSString *aString in array) {
      NSLog(@"Value: %@",aString);
   }

   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果 - 
2018-11-16 06:09:09.615 main[180842] Value: string1
2018-11-16 06:09:09.618 main[180842] Value: string2
2018-11-16 06:09:09.618 main[180842] Value: string3
2018-11-16 06:09:09.618 main[180842] Value: yii
2018-11-16 06:09:09.618 main[180842] Value: bai
Shell
如在输出中看到的那样,数组中的每个对象都按顺序打印。
快速枚举向后
for (classType variable in [collectionObject reverseObjectEnumerator] ) { 
  statements 
}
Objective-C
以下是快速枚举中reverseObjectEnumerator的示例 - 
#import <Foundation/Foundation.h>

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSArray *array = [[NSArray alloc]
   initWithObjects:@"string1", @"string2",@"string3",@"Yii",@"Bai",nil];

   for(NSString *aString in [array reverseObjectEnumerator]) {
      NSLog(@"Value: %@",aString);
   }

   [pool drain];
   return 0;
}
Objective-C
执行上面示例代码,得到以下结果 - 
2018-11-16 06:11:46.903 main[43643] Value: Bai
2018-11-16 06:11:46.904 main[43643] Value: Yii
2018-11-16 06:11:46.905 main[43643] Value: string3
2018-11-16 06:11:46.905 main[43643] Value: string2
2018-11-16 06:11:46.905 main[43643] Value: string1
Shell
正如您在输出中看到的那样,与正常快速枚举相比,数组中的每个对象都以相反的顺序打印。
//更多请阅读:https://www.yiibai.com/objective_c/objective_c_fast_enumeration.html


文章作者: 张赛东
文章链接: https://zsd.name
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 张赛东 !
评论
  目录