当前位置: 首页 > news >正文

基于Objectvie-C基础教程(第2版)做的笔记

基于Objectvie-C基础教程(第2版)做的笔记
第一个Objective-C 程序:
main.m:
#import <Foundation/Foundation.h>
int main(int argc, const char *argv[])
{
     NSLog(@"Hello, Objective-C");
     return(0);
}//main
 
#import语句:与c语言一样,Objective-C使用头文件来包含结构体/符号变量和函数原型等元素的声明。
#import是由XCode使用编译器提供的, #import可保证头文件只被包含一次, 无论此命令在该文件出现了多少次。
程序中的#import <Foundation/Foundation.h>语句告诉编译器查找Foundation框架中的Foundation.h头文件。
 
框架:是一种把头文件/库/图片/声音等内容聚集在一个独立单元中的集合体。一般的框架集有:Cocoa/Carbon/QuickTime/OpenGL。
(Cocoa的组成部分有Foundation和Application Kit也称AppKit)框架。
 
Foundation框架处理的是用户界面之下的那些层(Layer)的特性。比如数据结构和通信机制。
每 个框架都是一个重要的技术集合,通常包含数十个甚至上百个头文件。每个框架都有一个主头文件, 它包含了框架内所有的头文件。通过在主头文件中使用#import, 就可以访问框架内的所有功能。(在Headers目录:/System/Library/Frameworks/Foundaton.framework /Headers/可以看到Foundation框架包含了哪些头文件)
 
NSLog() 和 @“字符串”:
NSLog函数:
NSLog (@ "Hello, Objective-C!");
此 代码可向控制输入Hello Objective-C!. NSLog()这个函数的作用和C语言中的printf()很相似。与printf()一样,NSLog()接受一个字符串作为其第一个参数,该字符串可 以包含格式说明符(比如%d),此函数会接受与格式说明符相符相匹配的其他参数。(由于Objective-C只是添加了一些新特性的C语言,所以可以用 printf()来代替NSLog())
 
NS前缀避免名称冲突。
Cocoa给其所有函数、常量和类型名称都添加了NS前缀。这个前缀告诉我们函数来自Cocoa而不是其他工具包。
Cocoa已占用了NS前缀,所以很明显你不能再给你的任何变量和函数名称添加前缀NS, 否则会让阅读代码的人产生混淆,会以为创建的内容实际上是属于Cocoa的。
 
NSLog (@ "Hello, Objective-C!");
@符号是Objective-C在标准C语言基础上添加的特性之一。意味着引号内的字符串应作为Cocoa的NSString元素来处理。
 
NSString:是Cocoa中的字符串。
NSArray存放数组、NSDateFormatter帮助你用不同的方式来设置时间格式、NSThread提供多线程编程工具。NSSpeechSynthesizer能够让你听到语音。
 
布尔类型:
Objective-C提供的布尔类型是BOOL,它具有YES和NO两个值。C语言的bool类型和Objective-C语言的BOOL类型可以在同一个程序中共存,但是如果编写的是Cocoa代码,就只能使用BOOL。
项目的创建步骤:
main.m文件:
#import <Foundation/Foundation.h>
// return NO if the two integers have the sam value, Yes otherwise.
 
BOOL areIntsDifferent (int thing1, int thing2)
{
     if (thing1 == thing2) {
          return(NO);
     } else {
          return (YES);
     }
} // areIntsDifferent given a NO value, return the human-readable
// string "NO". Otherwise return "YES"
// 这里返回的值只能是BOOL类型的值:YES(1)或NO(0)
NSString *boolString (BOOL yesNo)
{
     if (yesNo == NO) {
          return (@"NO");
     } esle {
          return(@"YES");// 由于函数的返回类型是NSString类型,所以加上@“”,以表示返回的是字符串。
     }
}// boolString
 
int main(int argc, const char *argv[]) 
{
     BOOL areTheyDifferent;// 声明一个BOOL的值。
     areTheyDifferent = areIntsDifferent (5, 5);
     NSLog(@"are %d and %d different? %@", 5, 5, boolString(areTheyDifferent));
     // 这里的两个5将替代两个%d的占位符。
     // 在NSLog()的字符串的末尾,可以看到另一个@符号,这次表现为%@,它表示boolString()返回的是一个NSString指针。printf()不支持
     // 输出NSString,所以没有能够使用的格式说明符。NSLog()的编写者添加%@格式的说明符,是为了通知NSLog()接受适当的参数,将其
     // 作为NSString, 并使用该字符串中的字符,将其发送到控制台中。
 
     areTheyDifferent  = areIntsDifferent (23, 42);
     NSLog (@"are %d and %d different? %@", 23, 42, boolString(areTheyDifferent));
 
     return(0);
}
 
面向对象(Object-Oriented Programming)即OOP,是一种编程架构, 可构建由多个对象组成的软件。对象就好比存在于计算机中的小零件,它们通过互相传递信息来完成工作。
 
OOP的一个关键概念:间接(indirection),间接这个词看似隐晦,其实意思非常简单--在代码中通过指针间接获取某个值。
 
变量与间接:基本变量就是间接的一种实际使用。
Code:
#import <Foundation/Foundation.h>
 
int main(int argv, const char *argv[])
{
     NSLog(@"The numbers from 1 to 5: ");
     
     for (int i = 1; i <= 5; i++) {
          NSLog(@"%d\n", i);
     }
 
     return (0);
}
 
从上面的code可以看出,如果想要打印1到10, 就得更改NSLog函数里的字符串描述和for循环里的5改成10,但是如果当程序的代码量大的时候如果要修改,那么就会很麻烦,而变量就是用来解决此类问题的。
Code:
#import <Foundation/Foundation.h>
 
int main(int argv, const char *argv[])
{
     int count = 5;
     NSLog(@"The numbers from 1 to %d: ", count );
     
     for (int i = 1; i <= count ; i++) {
          NSLog(@"%d\n", i);
     }
 
     return (0);
}
 
使用文件名的间接,文件是间接的另一个示例。
Code:
#import <Foundation/Foundation.h>
 
int main(int argv, const char *argv[])
{
     const char *word[4] = { "aardvark",  "abacus", "allude", "zygote" };
     int wordCount = 4;
     
     for (int i = 0; i < wordCount; i++) {
 
          NSLog(@"%s is %lu characters long", words[i], strlen(words[i]));
 
          // 使用%s是因为words是C字符串数组,而不是@“NSString”对象的数组。
          // %lu格式说明符取计算字符串长度的strlen()函数的整数值,并输出单词及其长度。
     }
 
     return (0);
}
在上面的程序中,word数组的编辑需要注意标点符号,比如字母间的引号和输入项之间的逗号。如:
构建此程序的另一种方法就是将所有名字都移到代码之外的某个文件中,每行一个名字。没错,这就是间接。无需将名字直接放入源代码,而是让程序在其他地方查找这些名字。
Code:
#import <Foundation/Foundation.h>
 
int main(int argv, const char *argv[])
{
     FILE *wordFile = fopen ("/tmp/words.txt", "r");
     // fopen()函数打开word.txt文件以便读取内容, 然后
     char word[100];
     
     while (fgets(word, 100, wordFile))
          // fgets()从文件中读取一行文本并将其放入字符数组word中。fgets()调用
          // 会保留每行之间用来断行的换行符,如果留下它,换行符就会被计为单词中的一个字符。
          // 所以我们把换行符替换为\0,这表示字符串的结束。
     {
          // strip off the trailing \n
          word[strlen(word) - 1] = '\0';
          
          NSLog(@"%s is %lu characters long", word, strlen(word));
     }
 
     fclose (wordFile);
     return (0);
}
再次修改程序,不再通过/tmp/word.txt路径获取单词,修改程序, 使其查看程序的第一个启动参数,以确定单词文件的位置。
Code:
#import <Foundation/Foundation.h>
 
int main(int argc, const char *argv[])
{
     if (argc == 1) {// 如果用户提供路径,argc会大于1.
          NSLog(@"you need to provide a file name");
          return (1);
     }
 
     FILE *wordFile = fopen(argv[1], "r");//argv[0]是程序名。
     char word[100];
     while (fgets(word, 100, wordFile)) //这里的100是指截取一行里的100个字符,如果一行的字符不满100,就取这一行的所有字符。
     {
          // strip off the trailing \n
          word[strlen(word) - 1] = '\0';
          NSLog(@"%s is %lu characters long", word, strlen(word));
     }
     fclose(wordFile);
     return(0);
}
 
过程式编程(Procedual Programming)
Code:
#import <Foundation/Foundation.h>
// constants for the different kinds of shapes and their colors

typedef enum {
     kCircle,
     kRectangle,
     kOblateSpheroid
} ShapeType;


typedef enum {
     kRedColor,
     kGreenColor,
     kBlueColor
} ShapeColor;


// --------------------------------------------------
// Shape bounding rectangle

typedef struct {
     int x, y, width, height;
} ShapeRect;


// --------------------------------------------------
// The Shape

typedef struct {
     ShapeType type;
     ShapeColor fillColor;
     ShapeRect bounds;
} Shape;


// --------------------------------------------------
// convert from the ShapeColor enum value to a human-readable name

NSString *colorName (ShapeColor colorName)
{
    switch (colorName) {
        case kRedColor:
            return @"red";
            break;
        case kGreenColor:
            return @"green";
            break;
        case kBlueColor:
            return @"blue";
            break;
    }
   
    return @"no clue";
   
} // colorName


void drawCircle (ShapeRect bounds, ShapeColor fillColor)
{
     NSLog (@"drawing a circle at (%d %d %d %d) in %@",
             bounds.x, bounds.y,
             bounds.width, bounds.height,
             colorName(fillColor));
    
} // drawCircle


void drawRectangle (ShapeRect bounds, ShapeColor fillColor)
{
     NSLog (@"drawing a rectangle at (%d %d %d %d) in %@",
             bounds.x, bounds.y,
             bounds.width, bounds.height,
             colorName(fillColor));
    
} // drawRectangle


void drawEgg (ShapeRect bounds, ShapeColor fillColor)
{
     NSLog (@"drawing an egg at (%d %d %d %d) in %@",
             bounds.x, bounds.y,
             bounds.width, bounds.height,
             colorName(fillColor));
    
} // drawEgg


// --------------------------------------------------
// Draw the shapes

void drawShapes (Shape shapes[], int count)
{
     for (int i = 0; i < count; i++) {
         
          switch (shapes[i].type) {
                   
               case kCircle:
                    drawCircle (shapes[i].bounds,
                                   shapes[i].fillColor);
                    break;
                   
               case kRectangle:
                    drawRectangle (shapes[i].bounds,
                                      shapes[i].fillColor);
                    break;
                   
               case kOblateSpheroid:
                    drawEgg (shapes[i].bounds,
                              shapes[i].fillColor);
                    break;
          }
     }
    
} // drawShapes


// --------------------------------------------------
// The main function.  Make the shapes and draw them

int main (int argc, const char * argv[])
{
     Shape shapes[3];
    
     ShapeRect rect0 = { 0, 0, 10, 30 };
     shapes[0].type = kCircle;
     shapes[0].fillColor = kRedColor;
     shapes[0].bounds = rect0;
    
     ShapeRect rect1 = { 30, 40, 50, 60 };
     shapes[1].type = kRectangle;
     shapes[1].fillColor = kGreenColor;
     shapes[1].bounds = rect1;
    
     ShapeRect rect2 = { 15, 18, 37, 29 };
     shapes[2].type = kOblateSpheroid;
     shapes[2].fillColor = kBlueColor;
     shapes[2].bounds = rect2;
    
     drawShapes (shapes, 3);
    
     return (0);
    
} // main
 
上面的程序是使用过程式编程,但是编写这种代码必须注意要为各种数据类型使用正确的函数,并且程序的扩展和维护变得困难。
如果上面的代码要添加一个三角形的支持,那么要在4个地方进行添加。这还是程序小的时候,如果程序越复杂,扩展起来就越麻烦。
 
实现面向对象编程
 
过程式编程建立在函数之上, 数据为函数服务, 而面向对象编程则以程序的数据为中心, 函数为数据服务。在OOP中,不再重点关注程序的函数, 而是专注于数据。
 
用面向编程的思想来编写上面的程序:
这个时候在程序中的drawShapes方法是这样写的:
// Draw the shapes

void drawShapes (id shapes[], int count)
{
     for (int i = 0; i < count; i++) {
          id shape = shapes[i];
          [shape draw];
     }
    
} // drawShapes
该函数包含一个循环,用于遍历数组中的每个形状。在循环过程中,程序通知形状对象绘制自身。
有两个不同点,一是代码简洁了很多。另一个是函数的第一个参数shapes[],此时它是一个id数组对象。
id表示的是标识符(identifier)。 是一种泛型,可以用来引用任何类型的对象。对象是一种包含代码的struct结构体, 因此id实际上是一个指向结构体的指针。
 
 id shape = shapes[i];--这一行代码从shapes数组获取id(即指向某个对象的指针),并将其赋值给类型为id名为shape的变量。是一种指针的赋值过程。
 
[shape draw];--在C语言中方括号是指数组,但是在Objective-C还有其他意义: 用于通知某个对象该去做什么。方括号里的第一项是对象,其余部分是需要对象执行的操作。
 
在Objective-C中,通知对象执行某种操作称为 发送消息(也称”调用方法“)。代码[shape draw]表示向shape对象发送了draw消息。向对象发送消息后,如何调用所需的代码,这就需要叫做类的幕后帮手来协助完成。
 
 
circle对象含有一个指向其类的指针。 类是一种能够实例化成对象的结构体。Circle类含有一个指针指向用于绘制圆形、计算圆形的面积以及实现其他与圆形相关的必要功能的代码。如果在运行时改变某个类,则该类的所有对象会自动继承这些变化。
 
Objective-C语言中的OOP
@interface部分:
创建某个特定类的对象之前,Objective-C编译器需要一些有关该类的信息,尤其是对象的数据成员(即对象的C语言类型结构体应该是什么样子)及其提供的功能。可以使用@interface指令把这些信息传递给编译器。
Circle类的接口:
 
@interface Circle :NSObject
{
     @private
     ShapeColor fillColor;
     ShapeRect bounds;
}
 
- (void) setFillColor : (ShapeColor) fillColor;
 
- (void) setBounds: (ShapeRect) bounds;
 
- (void) draw;
 
@end // Circle
Code分析:
 
@interface Circle : NSObject -- 在Objective-C中只要看到@符号,就可以把它看成是对C语言的扩展。@interface Circle告诉编译器:“这是新类Circle的接口”。
@interface行中的NSObject告诉编译器,Circle类是基于NSObject类的。该语句表明每个Circle类都是一个NSObject, 并且每个Circle类都将继承NSObject类定义的所有行为。
{
  ShapeColor fillColor;
  ShapeRect bounds;
}
 
花括号中的内容是用于大量创新Circle对象的模板。它表示创建的新对象由两个元素构成。
在类声明中指定fillColor和bounds后,每次创建Circle对象,对象中都将包括这两个元素。fillColor和bounds的值称为Circle类的 实例变量(instance variable)
 
- (void) draw;
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
在Objective-C中,它们称为方法声明(method declaration)。方法声明列出了每个方法的名称、方法返回值的类型和某些参数。
声明前面的短线表明这个是Objective-C方法的声明。这个是区分函数原型与方法声明的一种方式,函数原型中没有先行短线。
短线后是方法的返回类型,位于圆括号中。void 表示无返回值。Objective-C方法可以返回与C函数相同的类型:标准类型(整型、浮点型和字符串)、指针、引用对象和结构体。
 
- (void) setFillColor: (ShapeColor) fillColor;在这个方法声明里, 这个方法还有一个参数fillColor。
声明以常见的先行短线和位于圆括号中的返回值类型开头:- (void)
方法名称是setFillColor:,结尾处的冒号是名称的一部分,它告诉编译器后编程人员后面出现参数。
(ShapeColor) fillColor;--参数类型是在圆括号中指定的。fillColor是参数名。
 
 
最后一行代码:@end告诉编译器,我们已经完成了Circle类的声明。
 
@implementation部分
上面的@interface部分,它用于定义类的公共接口。通常接口被称为API(application programming interface)。而真正使对象能够运行的代码位于@implementation部分中。
 
Circle类实现:
@implementation Circle
 
- (void) setFillColor: (ShapeColor) c
{
     fillColor = c;
}// setFillColor
 
-(void) setBounds: (ShapeRect) b
{
     bounds = b;
}// setBounds
 
Code分析:
@implementation Circle -- @implementation是一个编译器指令,表明你将为某个类提供代码。类名出现在@implementation之后。该行的结尾没有分号,因为在Objective-C编译器指令后不必使用分号。
接下来是各个方法的定义。它们不必按照在@interface指令中顺序出现。甚至可以在@implementation中定义那些在@interface中没有声明过的方法。可以把它们看作是仅能在当前类实现中使用的私有方法。
 
定义第一个方法:setFillColor:
 
- (void) setFillColor: (ShapeColor) c
{
     fillColor = c;
}
与@interface部分的声明非常类似。主要区别是结尾处有没有分号。
在@implementation中,我们把参数重新命名为简单的字符c了。@interface和@implementation间的参数名不同是允许的。如果继续使用fillColor,就会覆盖fillColor实例变量,并且编译器会生成警告信息。
在@interface部分的方法声明中使用名称fillColor,是为了告诉读者参数的真正作用。在实现中,我们必须区分参数名称和实例变量的名称。
 
在 Objective-C中调用方法时,一个名为self的秘密隐藏参数将被传递给接收对象,而这个参数引用的就是该接收对象,也就是说self是类的隐藏 的参数,指向当前调用方法的类。例如,在代码[circle setFillColor: kRedColor]中, 方法将circle作为其self参数进行传递。因为self的传递过程是秘密和自动的,所以不用程序员去实现。方法中引用实例变量的代码与下面的相似: self -> fillColor = c;
 
传递隐藏的参数是间接操作的有一个示例,因为Objective-C运行时(runtime)可以将不同的对象当成隐藏的self参数传递,所以那些对象的实例变量发生更改时,运行时也可以进行相应的更改。
最后一个方法draw,方法名结尾处没有冒号,说明它不适用任何参数。
- (void) draw
{
     NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
 
实例化对象:
实例化(instantiation).实例化对象时,需要分配内存,然后将这些内存初始化并保存为有用的默认值。这些值不同于通过新分配的内存获得的随机值。内存分配和初始化工作完成之后,就意味着新的对象实例已经创建好了。
以下是main()函数,它用于创建圆形,矩形,椭圆形
 
int main(int argc, const char *argv[])
{
     id shapes[3],
     
     ShapeRect rect0 = {0, 0, 10, 30};
     shapes[0] = [Circle new];// new一个类对象。
     [shapes[0] setBounds: rect0];// 执行对象的方法
     [shapes[0] setFillColor: kRedColor];
 
     ShapeRect rect1 = {30, 40, 50, 60};
     shapes[1] = [Rectangle new];
     [shapes[1] setBounds: rect1];
     [shapes[1] setFillColor: kGreenColor];
 
     ShapeRect rect2 = {15, 19, 37, 29};
     shapes[2] = [Egg new];
     [shapes[2] setBounds: rect2];
     [shapes[2] setFillColor: kBlueColor];
 
     drawShapes(shapes, 3);
     return (0);
}
这个时候如果想要添加一个新的三角形类,就比较简单了,仅需要完成两件事:创建Triangle类,然后将Triangle对象添加到将要绘制的对象列表中。
 
上 面的代码正好验证了面向对象编程大师Bertrand Meyer的开放/关闭原则(Open/Closed Principle),即软件实体应该对扩展开放,而对修改关闭。drawShapes()函数对扩展是开发的。仅需向数组添加要绘制的某个新型状。同 时,drawShapes()也是对修改关闭的,我们可以扩展它,而不必修改它。
 
继承:
 
处理类和对象的关系时,尤其要重视OOP的两个方面。第一个方面是 继承(inheritance).使用继承可以定义一个具有父类所有功能的新类,即它继承了父类的功能。
另一个和类相关的OOP技术是复合(composition),也就是在对象中可以再引用其他对象。
OOP中的继承表明一个类从另一个类--它的父类或超类(superclass)--中获取了某些特性。
 
继承的语法格式
@interface Circle: NSObject--冒号后面的标识符是需要继承的类。
在Objective-C中,你可以选择不继承,但如果使用的是Cocoa框架,就需要继承NSObject类,因为它提供了大量有用的特性。(当继承一个继承自NSObject类的类时,也可以获取这些特性)
 
只能继承一个:某些编程语言(例如C++)具有多继承性,也就是一个类可以直接从两个或多个类继承而来。但Objective-C不支持多继承。
@interface Circle :NSObject, PrintableObject 这样是编译不过的。
当然可以通过Objective-C的其他特性来达到多继承的效果。例如类别(category)和协议(protocol)。
 
根据继承,修改上面的代码:
Circle和Rectangle的接口代码更改为:
 
@interface Circle :Shape
@end // Circle
 
@interface Rectangle : Shape
@end // Rectangle
--程序中没有声明实例变量时可以省略花括号。
 
Shape类的接口声明代码:

@interface Shape : NSObject {
     ShapeColor fillColor;
     ShapeRect bounds;
}
-(void) setFillColor:(ShapeColor) fillColor;
-(void) setBounds: (ShapeRect)bounds;
-(void) draw;
 
@end // Shape
 
Shape类的实现代码:
@implementation Shape
 
-(void) setFillColor: (ShapeColor) c
{
     fillColor = c;
}// setFillColor
 
-(void)setBounds:(ShapeRect)b
{
     bounds = b;
} // setBounds
 
-(void) draw 
{
}// draw
@end // Shape
--虽然draw方法没有实现任何功能,但仍然需要定义它,以便Shape的所有子类都能通过它来实现各自的方法。
在方法的定义中不写任何内容或返回一个虚(dummy)值都是可以通过编译的。
 
Circle类的实现:
 
@implementation Circle
-(void) draw
{
   NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor)); 
} // draw
@end // Circle
 
Rectangle类的实现
@implementation Rectangle
-(void) draw
{
     NSLog(@"drawing a rect at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
} // draw
 
@end // Rectangle
 
继承的工作机制
方法调度:
对象在收到消息时,如何知道要执行哪个方法?-- 当代码发送消息时,Objective-C的方法调度机制将在当前的类中搜索相应的方法,如果无法在接受消息的对象的类文件中找到相应的方法,它就会在该对象的超类中进行查找。
没 有引入Shape父类的程序中,对于类似[shape setFillColor: kRedColor]的代码,Objective-C 的方法调度机制首先会寻找接受消息的对象,在本例中的Circle对象拥有一个指向Circle类的指针,Circle类也有一个指向其相应代码的指针。 调度程序通过这些指针来查找正确的代码。
 
增加了继承的新架构:
 
当 向Circle类的对象发送setFillColor:消息时,调度程序首先询问Circle类中的代码能否响应setFillColor:消息,不能的 话,调度程序会溯流而上进入Shape类中,找到setFillColor:定义,之后运行这段代码。如果在最顶层的NSObject类中也没有找到该方 法,则会出现一个运行时错误,同时还会出现一个编译时(compile-time)警告信息。
 
实例变量
 
为了理解实例变量的继承机制,创建一个新的形状,接口代码如下:
 
@interface RoundedRectangle  : Shape
{
     @private
     int radius;
}
@end // RoundedRectangle
 
RoundedRectangle对象的内存布局。NSObject类声明了一个名为isa的实例变量,该变量保存了一个指向对象当前类的指针。
 
记住,每个方法调用都获得了一个名为self的隐藏参数,它是一个指向接收消息的对象的指针。这些方法通过self参数来寻找它们需要用到的实例变量。
 
 
重写方法
 
当父类提供了一个方法但让该方法的实现内容为空,这个每个子类都能实现各自的功能,当子类实现各自的方法时,就说它们重写了父类的方法。
 
super关键字
Objective-C提供了一种方法,既可以重写方法的实现,又能调用超类中的实现方式。为了调用继承的方法在父类中的实现,需要使用super作为方法调度的目标。
 
绘制的圆都是绿色的:
Circle类的@interface部分不需要修改,只需向@implementation部分添加以下代码:
@implementation Circle
-(void) setFillColor:(ShapeColor) c
{
     if (c == kRedColor)
     {
          c = kGreenColor;
     }
     [super setFillColor: c];
}// setFillColor
@end // Circle
在这个新的setFillColor:实现中,我们会检查ShapeColor类型的参数是否是红色,如果是,就将它改成绿色,然后请求超类响应消息[super setFillColor:c]并将该颜色放入实例变量中。
 
super 既不是参数也不是实例变量,而是由Objective-C编译器提供的一种神奇的功能。当向super发送消息时,实际上是在请求Objective-C 向该类的超类发送消息。如果超类中没有定义该消息,Objective-c会和平常一样继续在继承链上一级查找。
 
 
 

转载于:https://www.cnblogs.com/hengshu/p/5007550.html

相关文章:

  • S3C2440-LCD图片显示
  • 1203有穷自动机
  • eclipse 导入tomcat7源码
  • 硬件黑客
  • Python生成器实现杨辉三角打印
  • jQuery获取表格第一列的值
  • linux 安装mysql
  • 100个vc小项目开发:二、一步一点设计音乐播放器 [I]
  • 升级Windows10后Apache服务器启动失败的解决方法
  • 记2个月来,我在Csdn 掀起的微软面试风暴
  • Bootstrap显示代码的三种效果
  • 经典算法研究系列:二、Dijkstra 算法初探
  • 神经网络(13)--具体实现:random initialization
  • 以软件开始生命周期来说明不同的测试的使用情况
  • SSH远程会话管理工具 - screen使用教程
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • eclipse(luna)创建web工程
  • JAVA SE 6 GC调优笔记
  • JavaScript中的对象个人分享
  • Less 日常用法
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Rancher-k8s加速安装文档
  • Redis 懒删除(lazy free)简史
  • XForms - 更强大的Form
  • 强力优化Rancher k8s中国区的使用体验
  • 区块链共识机制优缺点对比都是什么
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ###STL(标准模板库)
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #每天一道面试题# 什么是MySQL的回表查询
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (2022 CVPR) Unbiased Teacher v2
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (四)库存超卖案例实战——优化redis分布式锁
  • (一)Dubbo快速入门、介绍、使用
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET 反射的使用
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .NetCore项目nginx发布
  • .NET中winform传递参数至Url并获得返回值或文件
  • .stream().map与.stream().flatMap的使用
  • @EnableAsync和@Async开始异步任务支持
  • @GetMapping和@RequestMapping的区别
  • @ModelAttribute使用详解
  • @SuppressWarnings(unchecked)代码的作用
  • [\u4e00-\u9fa5] //匹配中文字符
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn