Contents

从C++到Objective-C

  • #import = #include#pragma once

  • 继承的时候没有限定符,继承都是 public 的。

  • 成员变量/函数没有限定符,成员变量缺省是 protected 的,而函数是 public 的。

  • 没有const , virtual,Objective-C 中函数缺省的就是 virtual 的。

  • Objective-C 中 方法前面的“-”是实例方法(类似于C++中的类成员函数)。

  • Objective-C 中 前缀为“+”的是类方法(类似于C++中的静态成员函数,或者是全局函数)。

  • @后的关键字

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    @interface:
    用于声明一个类的接口。它指定了类可以执行的方法(消息)和可以访问的实例变量。
    
    @protocol:
    用于声明一个或多个方法组成的协议。类可以选择遵循这些协议,并提供协议中定义的方法的实现。
    
    @implementation:
    用于提供在 @interface 中声明的类的方法的具体实现。
    
    @class:
    用于在头文件中声明一个类的存在,但不包含其完整定义。这允许在类定义之前引用该类,以避免循环依赖。
    
    @end:
    用于标记 @interface@implementation 或 @protocol 的结束。
    
    @selector:
    用于创建一个 SEL 类型的值,该值表示 Objective-C 中的一个方法选择器。SEL 类型是一个指向 IMP(方法实现)的指针的指针。
    
    @property:
    用于在类的接口中声明属性。属性可以是实例变量和访问器方法的简洁声明。
    
    @synthesize:
     Objective-C 2.0 及以后的版本中,编译器会自动为声明的属性生成访问器方法。但在某些情况下,你可能需要手动使用 @synthesize 来控制访问器方法的实现。
    
    @dynamic:
    告诉编译器属性的访问器方法将在运行时动态地解析。这通常用于 Core Data 等框架中,其中属性的访问器方法是由框架动态提供的。
    
    @private, @protected, @public:
    用于在类的接口中指定实例变量的访问级别。默认情况下,实例变量是私有的(@private),但可以使用这些关键字来修改其可见性。
    
    @encode:
    用于获取类型编码,这是一个表示 C 语言类型的字符串。这通常用于 Objective-C 运行时函数,如 objc_msgSend
    
    @try, @catch, @finally, @throw:
    用于实现异常处理。这些关键字提供了在 Objective-C 中捕获和处理异常的能力。
    
    @autoreleasepool:
    在非主线程或手动内存管理环境中使用,以定义自动释放池的范围。在这个范围内的对象会在池被释放时发送 autorelease 消息。
    
    @synchronized:
    用于同步代码块,防止多个线程同时访问同一块代码。它确保在给定时间只有一个线程可以执行同步块中的代码。
    
    @compatibility_alias:
    用于为已存在的类定义一个别名。这允许在不修改现有代码的情况下,通过新名称访问旧类。
    
    @restrict:
    在属性声明中使用,以指定属性应仅通过指定的访问器方法访问。这可以确保属性的值只能通过特定的方式来修改或读取。
    
    @objc:
     Swift 中与 Objective-C 交互时使用,用于标记 Swift 类、协议、方法或属性应暴露给 Objective-C 运行时。
    
  • 一段代码

    1
    2
    3
    4
    5
    6
    
    @interface TestTimer : NSObject
    
    - (void)startTimer;
    - (void)stopTimer;
    
    @end
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    # @interface TestTimer ():这是一个类扩展(class extension)的定义。它允许你为类添加额外的属性、方法或协议,而不必在公共接口中暴露它们。这里,你添加了一个名为timer的属性。
    @interface TestTimer ()
    # 定义了一个名为timer的属性,类型为NSTimer *
    # nonatomic:这是一个性能优化选项,表示该属性的访问不是线程安全的。在单线程环境中,它通常比atomic更快。
    # strong:表示这个属性是一个引用计数类型的属性,并且它拥有其引用的对象。当timer属性被设置为新的NSTimer对象时,该对象的引用计数会增加;当timer被设置为nil时,其引用计数会减少,如果减少到0,则该对象会被释放。
    @property (nonatomic, strong) NSTimer *timer;
    
    # assign 用于简单的数据类型,如 NSInteger、CGFloat、char、int、float、double、CGPoint、CGRect、CGSize 等,以及 C 语言的指针类型(如 id、NSObject *、NSString * 等)。
    # 使用 assign 时,只是简单地设置变量值,不会调用任何额外的内存管理方法(如 retain、release 或 autorelease)。
    # 对于对象类型(如 NSString *、NSArray * 等),使用 assign 是不安全的,因为它不会增加对象的引用计数,从而可能导致野指针(dangling pointer)或内存泄漏。对于对象类型,通常应该使用 strong 或 weak。
    @property (nonatomic, assign) NSInteger count; 
    @end
    
    # 这是类的实现部分,包含了所有方法的具体实现。
    @implementation TestTimer
    
    # 这个方法用于启动一个定时器。
    - (void)startTimer {
        if (!_timer) {
           # selector:@selector(printCurrentTime):当定时器触发时,会调用printCurrentTime方法。
           # userInfo:nil:传递给定时器触发方法的数据,这里为nil。
           # repeats:YES:定时器是否应该重复触发。这里设置为YES,表示每秒都会触发一次。
           # 调用[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];实际上是不必要的,因为[NSTimer scheduledTimerWithTimeInterval:...]方法已经自动将定时器添加到当前运行循环的默认模式中。但如果你在其他情况下需要手动添加定时器,这段代码会很有用。
    
          # userInfo:@{@"count": @(self.count)}
          # 这里创建了一个字典(使用 Objective-C 的字面量语法),其中包含一个键为 @"count",其对应的值是通过 @(self.count) 将 NSInteger 类型的 self.count 转换为 NSNumber 类型的对象。这是因为字典的值必须是对象类型,而 NSInteger 是一个基本数据类型,所以需要将其“装箱”(boxing)为 NSNumber 对象。
            _timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                      target:self
                                                    selector:@selector(printCurrentTime)
                                                    userInfo:@{@"count": @(self.count)}
                                                     repeats:YES];
            [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
        }
    }
    
    - (void)stopTimer {
        if (_timer) {
            [_timer invalidate];
            _timer = nil;
        }
    }
    
    - (void)printCurrentTime {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSString *currentTime = [dateFormatter stringFromDate:[NSDate date]];
        NSLog(@"Current Time: %@, Count: %ld", currentTime, (long)self.count); 
    }
    
    @end
    
  • [car fly];

    • C++ : 调用car类别的fly方法,若car类别里头没有定义fly方法,那编译肯定不会通过。
    • Objective-C: “发提交一个fly的消息给car对象”,fly是消息,而car是消息的接收者。car收到消息后会决定如何回应这个消息,若car类别内定义有fly方法就运行方法内之代码,若car内不存在fly方法,则程序依旧可以通过编译,运行期则抛出异常。
  • NSString :Objective-C提供了一个助记符可以方便地从常量值创建NSString对象。要使用这个助记符,你需要做的全部事情,是在普通的双引号字符串前放置一个@符号

    1
    2
    3
    4
    5
    6
    
    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];
    
  • Objective-C 的类规格说明包含了两个部分:定义(interface)与实现(implementation)。定义(interface)部分包含了类声明和实例变量的定义,以及类相关的方法。实现(implementation)部分包含了类方法的实际代码。

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

    • 方法声明语法

      如果子类定义了跟父类的具有相同标识符的方法,那么子类首先收到消息,然后可以有选择的把消息转发(也可以不转发)给他的父类。

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

      1
      
      [myArray insertObject:anObj atIndex:0];
      

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

      1
      
      [[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];
      
  • 协议

  • id : C++里的(void*)类似