ObjC的initialize和init

本文探讨了Objective-C中initialize方法的特点及使用方式,并通过实例对比了它与init的区别,特别是当存在子类时initialize方法的调用行为。

Objective-C很有趣的一个地方是,它非常非常像C。实际上,它就是C语言加上一些其他扩展和一个运行时间(runtime)。

有了这个在每个Objective-C程序中都会起作用的附加运行时间,给了它一些动态的特性。C和C++没有运行时间,他们仅仅被编译为完全按照代码的顺序去执行,不多也不少。
Objective-C中带有运行时间的好处是,它可以在你的程序运行的流程中进行参与。在Objective-C中,它包括检查是否一个对象可以处理特定的消息,如果不能处理,它就帮助你自动调用其他特定的方法去完成。

initialize不是init

运行时间的行为之一就是initialize。虽然看起来有点像大家常见的init,但是他们并不相同。

在程序运行过程中,它会在你程序中每个类调用一次initialize。这个调用的时间发生在你的类接收到消息之前,但是在它的超类接收到initialize之后。

举个例子,比如一个叫做Duck的类:

#import "Duck.h";
 
@implementation Duck
 
+(void) initialize {
    NSLog(@"Duck initialize");
}
 
-(void) init {
    NSLog(@"Duck init");
}
@end

我们在这里记录initialize和init调用的时间。

我们建立三个Duck对象的实例:

NSLog(@"Hello, World!");

Duck* duck1 = [[Duck alloc] init];

Duck* duck2 = [[Duck alloc] init];

Duck* duck3 = [[Duck alloc] init];
 
看一下记录:

[Session started at 2008-03-23 20:03:25 -0400.]

2008-03-23 20:03:25.869 initialize_example[30253:10b] Hello, World!

2008-03-23 20:03:25.871 initialize_example[30253:10b] Duck initialize

2008-03-23 20:03:25.872 initialize_example[30253:10b] Duck init

2008-03-23 20:03:25.873 initialize_example[30253:10b] Duck init

2008-03-23 20:03:25.873 initialize_example[30253:10b] Duck init

我们可以看到,虽然我们创建了3个Duck的实例,但是initialize仅仅被调用了一次。我们也可以看到,直到我们创建了一个Duck的实例,initialize才被调用。

但是如果Duck有一个子类的话,比如我们建一个Duck的子类叫做Chicken(好怪异……)

#import <cocoa/Cocoa.h>

#import "Duck.h"
 
@interface Chicken : Duck {
 
}
 
@end

注意Chicken这个类并没有实现initialize方法。

如果我们同样运行这个程序,但是加上一个Chicken的实例:

NSLog(@"Hello, World!");

Duck* duck1 = [[Duck alloc] init];
Duck* duck2 = [[Duck alloc] init];
Duck* duck3 = [[Duck alloc] init];
    
Chicken* chicken = [[Chicken alloc] init];
 
我们期待看到4个Duck的init调用(因为我们建立了3个Duck和一个Chicken),但是我们看到了这样情况:

[Session started at 2008-03-23 20:13:34 -0400.]

2008-03-23 20:13:34.696 initialize_example[30408:10b] Hello, World!

2008-03-23 20:13:34.698 initialize_example[30408:10b] Duck initialize

2008-03-23 20:13:34.699 initialize_example[30408:10b] Duck init
 
2008-03-23 20:13:34.700 initialize_example[30408:10b] Duck init
 
2008-03-23 20:13:34.700 initialize_example[30408:10b] Duck init
 
2008-03-23 20:13:34.700 initialize_example[30408:10b] Duck initialize
 
2008-03-23 20:13:34.701 initialize_example[30408:10b] Duck init

我们看到了4个Duck的init和2个Duck的initialize方法。这是怎么回事呢?

看来如果一个子类没有实现initialize方法,那么超类会调用这个方法两次,一次为自己,而一次为子类。

我们在Duck的initialize类中记录一下类名,这样可以看得更清楚:
 
+(void) initialize {
   NSLog(@"Duck initialize class:%@",[self class]);
}

现在看明白了:

[Session started at 2008-03-23 20:21:08 -0400.]
 
2008-03-23 20:21:08.816 initialize_example[30513:10b] Hello, World!
 
2008-03-23 20:21:08.818 initialize_example[30513:10b] Duck initialize class:Duck
 
2008-03-23 20:21:08.819 initialize_example[30513:10b] Duck init
 
2008-03-23 20:21:08.820 initialize_example[30513:10b] Duck init
 
2008-03-23 20:21:08.820 initialize_example[30513:10b] Duck init
 
2008-03-23 20:21:08.820 initialize_example[30513:10b] Duck initialize class:Chicken

2008-03-23 20:21:08.821 initialize_example[30513:10b] Duck init

如果你希望确定只用了initialize一次用来实现某些单独运行的工作,或者希望实现仅仅运行一次的方法,检查一下[self class],才能确定是否是你希望做到的效果。

<think>我们正在处理一个Objective-C ARC编译错误:Cannot initialize __strong id variable with rvalue of type HWSDKMag 这个错误通常发生在尝试将一个非Objective-C对象类型(比如C++类型)赋值给一个声明为`__strong id`的变量时。 根据错误信息,`HWSDKMag`类型不是一个Objective-C对象类型(即不是从NSObject继承的类),而ARC要求所有`id`类型的变量必须持有Objective-C对象指针。 可能的原因解决方案: 1. **检查HWSDKMag类型**:首先确认`HWSDKMag`是什么类型。如果它是一个C++类,那么它不能直接赋值给`id`类型,因为`id`要求是一个Objective-C对象指针。 解决方案:如果`HWSDKMag`确实是一个C++类,那么你可能需要将它包装在一个Objective-C类中(使用Objective-C++,即.mm文件),或者使用一个指向该C++对象的指针,并用`void*`来存储(但这样会失去ARC管理,需要手动管理内存)。 2. **使用Objective-C++**:如果项目允许,可以将涉及的文件后缀改为`.mm`,这样编译器会按照Objective-C++来编译,允许混合使用C++Objective-C。但是,即使这样,也不能直接将C++对象赋值给`id`类型。你需要创建一个Objective-C类来封装这个C++对象。 3. **封装成Objective-C对象**: 例如,创建一个新的Objective-C类,比如`MySDKMagWrapper`,它有一个属性来保存`HWSDKMag`的实例(可能是原始指针,或者智能指针,取决于所有权管理)。 ```objective-c // MySDKMagWrapper.h #import <Foundation/Foundation.h> // 前向声明,避免在头文件中包含C++头文件 struct HWSDKMag; @interface MySDKMagWrapper : NSObject - (instancetype)initWithSDKMag:(const HWSDKMag&)mag; @property (nonatomic, readonly) const HWSDKMag& sdkMag; @end // MySDKMagWrapper.mm - 注意这里使用.mm扩展名 #import "MySDKMagWrapper.h" #import "HWSDKMag.h" // 包含C++头文件 @implementation MySDKMagWrapper { HWSDKMag _mag; // 或者使用指针,根据实际情况 } - (instancetype)initWithSDKMag:(const HWSDKMag&)mag { self = [super init]; if (self) { _mag = mag; // 或者用new拷贝构造,取决于HWSDKMag的拷贝语义 } return self; } - (const HWSDKMag&)sdkMag { return _mag; } @end ``` 然后,你可以创建这个包装类的实例,并赋值给`id`类型的变量: ```objective-c HWSDKMag mag = ...; // 创建HWSDKMag实例 id obj = [[MySDKMagWrapper alloc] initWithSDKMag:mag]; ``` 4. **使用`void*`**:如果不想创建包装类,并且你能确保内存管理正确,可以使用`void*`来存储C++对象指针,但这样ARC不会管理它的生命周期,需要手动管理。 ```objective-c HWSDKMag* mag = new HWSDKMag(...); void* p = (void*)mag; // 使用后需要释放 delete static_cast<HWSDKMag*>(p); ``` 但是,这种方法不推荐,因为它容易导致内存泄漏,而且不能享受ARC的便利。 5. **检查是否应该使用`id`**:考虑这个变量是否真的需要是`id`类型。如果它只在C++上下文中使用,那么可能应该使用正确的类型而不是`id`。 6. **检查头文件**:确保在包含`HWSDKMag`的头文件时,编译器知道它是C++类型。在Objective-C文件中使用C++类型时,需要将文件扩展名改为`.mm`,并且使用`__cplusplus`宏保护。 7. **错误的具体位置**:查看错误发生的代码行,确认赋值操作的两边类型。如果左边是`id`,右边是`HWSDKMag`(不是指针),那么就需要将右边的值转换为对象指针,或者修改左边变量的类型。 根据错误信息,我们无法直接使用非对象类型初始化`__strong id`变量。因此,最常用的解决方案是使用包装类(第3点)或者将文件改为Objective-C++并确保类型正确(但即使这样,赋值给id仍然需要对象指针)。 注意:如果`HWSDKMag`实际上是一个Objective-C类,那么可能是头文件包含问题或者编译器没有正确识别。检查类声明是否包含正确的NSObject基类。 由于问题中给出的引用[1]提到ARC优化器会识别C函数,这提醒我们ARC对内存管理的严格性,因此必须确保类型系统正确。 请根据实际情况选择解决方案。 §§相关问题§§ 1. 如何将C++对象集成到Objective-C项目中? 2. Objective-C ARC对对象类型有哪些要求? 3. 如何手动管理Objective-C中C++对象的内存? 4. 在Objective-C++中,如何避免循环引用? 5. 如何检测Objective-C中的内存泄漏?</think>### 解决 Objective-C ARC 错误:Cannot initialize __strong id variable with rvalue of type HWSDKMag 这个错误通常发生在尝试将 **非 Objective-C 对象类型** 赋值给 ARC 管理的 `id` 类型变量时。以下是原因分析解决方案: #### 错误原因 1. **类型不兼容** `HWSDKMag` 可能是 C++ 类、C 结构体或非 NSObject 派生的类型,而 `__strong id` 要求必须是 Objective-C 对象指针(指向 `NSObject` 子类的实例)。 2. **所有权问题** ARC 要求 `__strong` 变量必须持有 Objective-C 对象,以便自动管理内存生命周期。非 Objective-C 类型无法被 ARC 跟踪。 3. **右值限制** 错误中的 `rvalue` 表示赋值操作的右侧不是可保留的 Objective-C 对象指针(如临时对象或值类型)。 --- #### 解决方案 ##### 方法 1:封装为 Objective-C 对象(推荐) ```objective-c // 创建包装类(文件后缀改为 .mm 启用 Objective-C++) @interface HWSDKMagWrapper : NSObject @property (nonatomic) HWSDKMag cppObject; // C++ 对象 @end @implementation HWSDKMagWrapper @end // 使用示例 HWSDKMag rawMag = CreateMag(); // 创建 C++ 对象 HWSDKMagWrapper *wrapper = [[HWSDKMagWrapper alloc] init]; wrapper.cppObject = rawMag; // 正确:ARC 管理包装类 id safeObject = wrapper; // 可赋值给 id ``` ##### 方法 2:使用桥接指针(手动管理内存) ```objective-c // 创建 C++ 对象 HWSDKMag* rawMagPtr = new HWSDKMag(); // 桥接为 void* 再转为 id(需手动释放) __strong id unsafeId = (__bridge id)rawMagPtr; // 使用后必须显式释放 delete (HWSDKMag*)unsafeId; // ⚠️ 高风险操作 ``` ##### 方法 3:修改变量类型(适用纯 C++ 场景) ```objective-c // 将 __strong id 改为实际类型 HWSDKMag mag = CreateMag(); // 直接使用 C++ 类型 // 或 HWSDKMag* magPtr = &mag; // 使用指针 ``` --- #### 关键注意事项 1. **文件扩展名** 若涉及 C++ 代码,将 `.m` 文件改为 `.mm` 启用 Objective-C++ 编译。 2. **内存安全** 方案 2 需手动管理内存,容易导致泄漏或野指针。优先选择封装方案。 3. **类型检查** 通过 `__has_feature(objc_arc)` 宏确认 ARC 是否启用: ```objective-c #if __has_feature(objc_arc) // ARC 代码 #else // MRC 代码 #endif ``` 4. **所有权修饰符** 避免混合使用 `__strong` 非对象类型,必要时用 `__unsafe_unretained` 修饰 C++ 指针: ```objective-c __unsafe_unretained HWSDKMag* weakPtr = rawMagPtr; ``` > 引用说明:ARC 对非 Objective-C 类型的限制源于其内存管理模型,编译器必须精确识别对象生命周期[^1]。C++ 对象与 ARC 的兼容性问题在混合编程中常见,需通过封装解决[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值