目标C在堆栈上分配对象



我是Objective C的新手,正在对对象进行一些练习。虽然像Fraction* f = [[Fraction alloc] init ];这样的东西可以工作,但每当我尝试执行Fraction c;时,我都会得到Interface type cannot be statically allocated。堆栈上是否有分配对象的方法(比如c++中的方法)?还是我做错了?

从技术上讲,您可以(参见下面的代码),但不应该。此外,无论是在堆栈还是堆中,都只有一个指向对象的指针,而不是对象本身。也就是说,应该编写Fraction *c,而不是Fraction c

// Allocate an Objective-C object on the stack.
// Original code By Graham Lee: 
// https://gist.github.com/iamleeg/5290797     
#import <Foundation/Foundation.h>
#include <stdlib.h>
#include <objc/runtime.h>
@interface A : NSObject
@property (assign) int meaning;
@end
@implementation A
- (id)init {
    if ([super init]) {
        _meaning = 42;
    }
    return self;
}
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        // allocate and zero stack memory
        size_t size = class_getInstanceSize([A class]);
        id obj = (__bridge_transfer id) alloca(size);
        memset((__bridge void*)obj, 0, size);
        // set class and initialize the object
        object_setClass(obj, [A class]);
        obj = [obj init];
        NSLog(@"meaning: %d", [obj meaning]);
        // transfer ownership from ARC to CF so ARC doesn't 
        // try to improperly free the stack allocated memory
        CFTypeRef ref = (__bridge_retained CFTypeRef) obj;
    }
}

alloca()是非标准的、不安全的、不可移植的,并且容易发生堆栈溢出。

不能在目标C中静态分配对象。这有很多原因。包括对象应该初始化,但允许初始化方法更改对象的地址。

在C++中,构造函数必须初始化它所调用的对象,并且不能以任何方式更改对象地址。这在目标C中是不正确的。等价的构造函数(alloc+init*序列或类级方法)可以决定他们将更改被调用对象的地址(当然,他们将负责释放原始对象)。

当然,他们没有办法释放静态分配的内存,也没有办法更改堆栈的地址。

否,所有对象都分配在Obj-C中的堆上。

否,不能将堆栈用于Objective-C对象的内存,它必须是堆(通过allocinit)。

虽然正如@wattson12所指出的,您显然可以将对象引用(指针)存储在堆栈上,但这仍然不会将堆栈用于对象的内存。

您得到的错误是由于语法错误,您需要执行以下操作:

Fraction *c; 

请注意,缺少的*表示这是一个指向对象的指针(您没有将此对象存储在堆栈上,您只是创建了一个对(潜在)对象的引用)

所有objective-c对象都分配在中,块对象除外。主要原因是:

  1. Objective-C中不跟踪指向对象的指针(相反,Objective-C使用引用计数系统进行内存管理);如果你移动对象,它们就无法更新
  2. stack对象将在创建它们的函数返回时被销毁

Mike Ash的一篇帖子有助于理解这一点。

@Jano使用普通C内存堆栈的答案的变体,使用Obj-C MRR模式(ARC已禁用,如果你想堆栈分配Obj-C对象,你真的应该仔细手动检查应用程序/库中的每一个保留/释放):

#import <Foundation/Foundation.h>
#include <objc/runtime.h>
#include <malloc/malloc.h>
@interface A : NSObject
@property (assign) int meaning;
@end
@implementation A
- (id)init {
    if ([super init]) {
        _meaning = 42;
    }
    return self;
}
- (void)dealloc
{
    BOOL wasStackAllocated = (malloc_zone_from_ptr(self) == NULL);
    if (!wasStackAllocated)
        [super dealloc];
}
@end

struct A_storage {
    uint8 bytes[16];
};


int main(int argc, char *argv[]) {
    @autoreleasepool {
        
        // allocate and zero stack memory
        assert(class_getInstanceSize(A.class) == sizeof(struct A_storage));
        struct A_storage obj_storage;
        id obj = (id)&obj_storage;
        memset(obj, 0, sizeof(struct A_storage));
        
        // set class and initialize the object
        object_setClass(obj, [A class]);
        obj = [obj init];
        
        NSLog(@"meaning: %d", [obj meaning]);
        
        // Pass `obj` to other code here, as long as you make sure those objects are
        // released and aren't holding any refs before continuing.
        
        [obj release];
        assert([obj retainCount] == 0);
    }
}

注意事项:这是一种非常疯狂和不受欢迎的行为,谁知道有什么注意事项,但Obj-C和Darwin的malloc是开源的,所以如果在实际的应用程序/lib使用中出现严重崩溃,你就应该阅读并了解引擎盖下发生了什么。

最新更新