1

As is known that in ObjC property declared in .h file is the interface "visible outside", while property declared in .m file (class extension) can be only accessed in .m, kind of "private" or "hidden". But in practice code as follows can be compiled.

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

The fact is, not only ClassA *aObj defined in ClassA's own method can access class extension property aInt, but ClassA *aObj defined in another _ClassB while in the same ClassA.m file can also access aInt.

As far as I understand, aObj defined in class method foo has no difference with any ClassA * type variable defined in another class and separate .m file. But by no means will the latter access 'aInt', say

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*'
}

Why is this happening? Can this be explained with ObjC runtime mechanism or something?

刘maxwell
  • 187
  • 10
  • Because `objective-c` language doesn't have private methods and properties. You can **simulate** accessibility only in compile time by hiding their declaration, but anyway in runtime they will be public. For example you can call private api and apple can forbid it only by rejecting distribution in appstore. – Cy-4AH Jan 09 '19 at 12:26

1 Answers1

1

It has nothing to do with the Objective C runtime. In fact, if you use key value coding, you can access any property and/or method from any class from any source file you want, may it be declared private or not, or in an extension or directly. This is how some people (forbiddenly) use private APIs of Apple.

Objective C, like C, just needs to know the declarations of your class. This is done by importing the header files. The header file says "Look, there is something like ClassA, it has these methods and those properties", and then you can use them.

Anything that is declared in a .m file is not visible to other source files, because you typically do not import .m files (although, technically, it works). Nevertheless, the declaration still exist - just the compiler does not know of it when compiling the other file.

You could create a dummy header file:

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

and then use it in your ClassC:

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

When compiling ClassC.m, the compiler get's to know that someting like aInt exists in ClassA. The linker - as the final step - then checks if this really is true, e.g. if one (and only one) of the compiled source files contained the definition of aInt.

Try this: Just declare a property that is not defined anywhere:

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

and then use it:

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

The compiler will compile the code, but the linker then will say that there is no definition of oopsDoesItExist

Just a final remark: You can define iVars or synthesize properties only in class extensions (anonymous categories) within an .m file. See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34