ObjC - Почему экземпляру разрешен доступ к свойству расширения класса в файле .m? - PullRequest
0 голосов
/ 09 января 2019

Как известно, свойство ObjC, объявленное в файле .h, является интерфейсом, «видимым снаружи», в то время как свойство, объявленное в файле .m (расширение класса), доступно только в .m, своего рода «частное» или «скрытое». ». Но на практике можно скомпилировать следующий код:

ClassA.h

@interface ClassA : NSObject
+ (void)foo;
@end

ClassA.m

#import "ClassA.h"

@interface ClassA ()
@property (nonatomic) NSInteger aInt;
@end

@implementation ClassA 
+ (void)foo {
    ClassA *aObj = [ClassA new];
    aObj.aInt = 2;  //?
}
@end

@interface _ClassB : NSObject  //Some private class defined in the same .m file...
@end
@implementation _ClassB

+ (void)bar {
    ClassA* aObj = [ClassA new];
    aObj.aInt = 2;  //?
}

@end

Дело в том, что не только ClassA *aObj, определенный в собственном методе ClassA, может получить доступ к свойству расширения класса aInt, но ClassA *aObj, определенный в другом _ClassB, тогда как в том же файле ClassA.m также может доступ aInt.

Насколько я понимаю, aObj, определенный в методе класса foo, не имеет разницы с любой переменной типа ClassA *, определенной в другом классе и отдельном файле .m. Но ни в коем случае последний не получит доступ 'aInt', скажем

ClassC.m

#import "ClassA.h"
...
- (void)fun {
   ClassA *aObj = [ClassA new];
   NSLog("%d", aObj.aInt);  //Error! Property aInt not found on object of type 'ClassA*'
}

Почему это происходит? Это можно объяснить механизмом выполнения ObjC или чем-то еще?

1 Ответ

0 голосов
/ 09 января 2019

Это не имеет ничего общего с временем выполнения Objective C. Фактически, если вы используете кодирование значения ключа, вы можете получить доступ к любому свойству и / или методу из любого класса из любого исходного файла, который вы хотите, может ли это быть объявлен частным или нет, или в расширении или напрямую. Вот как некоторые люди (запрещено) используют частные API Apple.

Цель C, как и C, просто должна знать объявления вашего класса. Это делается путем импорта заголовочных файлов. Заголовочный файл говорит: «Послушайте, есть что-то вроде ClassA, у него есть эти методы и эти свойства», и вы можете использовать их.

Все, что объявлено в файле .m, невидимо для других исходных файлов, поскольку вы, как правило, не импортируете файлы .m (хотя технически это работает). Тем не менее, декларация по-прежнему существует - просто компилятор не знает об этом при компиляции другого файла.

Вы можете создать фиктивный заголовочный файл:

// FakeClassAExtension.h
// ...
@interface ClassA (Fake)
@property (nonatomic) NSInteger aInt;
@end

и затем используйте его в своем ClassC:

// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension.h"
//...
- (void)fun {
    ClassA *aObj = [ClassA new];
    NSLog("%d", aObj.aInt);  //Fine
}

При компиляции ClassC.m компилятор узнает, что что-то вроде aInt существует в ClassA. Компоновщик - как последний шаг - затем проверяет, действительно ли это так, например, если один (и только один) из скомпилированных исходных файлов содержал определение aInt.

Попробуйте: просто объявите свойство, которое не определено в любом месте:

// FakeClassAExtension2.h
// ...
@interface ClassA (Fake2)
@property (nonatomic) NSInteger oopsDoesItExist;
@end

и затем используйте его:

// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension2.h"
//...
- (void)fun {
    ClassA *aObj = [ClassA new];
    NSLog("%d", aObj.oopsDoesItExist);  //Compiler is fine here
}

Компилятор скомпилирует код, но компоновщик скажет, что нет определения из oopsDoesItExist

Просто последнее замечание: вы можете определить iVars или синтезировать свойства только в расширениях классов (анонимных категориях) в файле .m. См https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

...