Objective-C protocol syntax - objective-c

Here are 2 lines of code from Apple's own SimpleStocks sample code (APLSimpleStockView.m)
NSInteger dataCount = [self.dataSource graphViewDailyTradeInfoCount:self];
NSArray *sortedMonths = [self.dataSource graphViewSortedMonths:self];
The first code line above looks like "dataSource" is the recipient of message graphViewDailyTradeInfoCount:self (which returns an NSInteger).
The second line of code above looks like "dataSource" is now the recipient of message graphViewSortedMonths:self (which returns an NSArray *).
The only reference to dataSource I can find (in APLSimpleStockView.h) has it being a property, not an object/class instance? How come I can send a property a message? I thought I can only get and set a property's value?
The end result of the code is that after line 1, dataCount contains a number, and after line 2, sortedMonths contains an array of sorted month names. But where does this behaviour come from, since I cant find any place in the sample where dataSource causes anything to be returned when sent a message.
Is self.dataSource acting as both a getter and a setter here?

I thought I can only get and set a property's value?
That's correct, but what's the value of a property? In this case it's an object, and you can definitely send a message to an object.
The code is equivalent to using the getter for the property, assigning the result to a variable, and then sending the message:
WhateverClassTheDataSourceIs * dS = self.dataSource;
NSInteger dataCount = [dS graphViewDailyTradeInfoCount:self];
The additional assignment just isn't necessary.
(Your code could also be written
[[self dataSource] graphViewDailyTradeInfoCount:self];
If that makes it clearer for you.)

How come I can send a property a message?
A property is just a promise to provide accessor methods for a given name. If the property is foo, the accessors are typically -foo and -setFoo:. So, in this case, self.dataSource returns an object that receives the message.
If you look at the APLSimpleStockView interface, you'll see the property declared as a pointer to an object:
#property (nonatomic, weak) IBOutlet id<APLSimpleStockViewDataSource> dataSource;
That means that dataSource is an id (that is, a pointer to an object) that implements the APLSimpleStockViewDataSource protocol. Also, it's marked as an outlet so that you can set it in Interface Builder. Accordingly, self.dataSource returns an id (again, an object pointer) that refers to the view's data source.
Is "self.dataSource acting as both a getter and a setter here?
No, it's just a getter returning the data source object.

Related

Keeping objectiveC object valid outside the scope of a function

I'm a bit confused about ARC behaviour when setting variable that is an input pointer, and is expected to remain valid outside function scope.
considering the following example that uses openDirectory framework.
#interface bbb
-(bool)doSomethingWithADRecord:
-(void)obtainADRecord(NSString*)user
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record;
#end
#interface bbb {
ODRecord *_myRecord;
}
#end
#implementation bbb
-(void)doSomethingWithADRecord:
{
// here we access _myRecord and expect it to be valid.
}
-(bool)obtainADRecord:(NSString*)user
{
...
// here I call the method that will set the member _myRecord from type ODRecord*
// whose scope related to the lifespan of the containing class (bbb)
[self getADrecord:attr toRecord:_myRecord];
}
// the following function should set the variable record to be used by the caller.
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record {
...
// here a set an ODQuery object.
ODQuery *query = [[ODQuery alloc] initWithNode ...
// queryResults is an array of items from type ODQuery*
NSArray* queryResults = [query resultsAllowingPartial:NO error:&err];
for(ODRecord *item in queryResults) {
if (/*some logic*/)
{
//option 1: just regular set operator, expecting the ARC will do the retain itself
record = item;
//option 2: explicits take a reference on that item.
record = [[item retain] autorelease];
return #"found item";
}
}
}
#end
To Clarify my question, I seek to know which one of the 2 options I stated above is the correct one , in terms of passing the reference to record and eventually to _myRecord, so it will store the correct value even after the temporal list of queryResults will be cleaned.
Notice that in both options I simply setting the pointer value without initiate new object from type ODquery and copying the data to this new object.
thanks !
I'd like to know whether simply doing record = item will be enough for the data pointed by this object to last beyond the scope of the function getADrecord
You are misunderstanding how parameters work; a parameter, such as record, is essentially a local variable which is initialised to the value passed in the call.
Therefore any assignment of an object reference to record will have zero effect on the lifetime of the referenced object outside of the scope of getADrecord as record is local to the function.
To return a value of type T via a parameter the type of the parameter must be of type "pointer to a variable of type T". An example with a simple value type:
- (void) add:(int)value // an int value
to:(int *)ptrToVariable // a pointer to an int variable
{
// note the need to indirect (`*`) through pointer stored in
// `ptrToVariable` to access the pointed at variable
*ptrToVariable = *ptrToVariable + value;
}
int x = 31;
[self add:11 to:&x]; // &x creates a pointer to the variable x
// x = 42 after call
Now you don't want to return a simple value type but a value which is a reference to an object and you wish ARC to manage the lifetime correctly. This is a little more complicated.
Under ARC a variable which holds a reference to an object has both a type and an ownership attribute; this attribute informs ARC how to handle storing references in the variable. The common ownership attributes are __strong and __weak, without an explicit attribute __strong is assumed. So your instance variable declaration is shorthand for:
ODRecord __strong *_myRecord;
This declaration means that for any reference to an ODRecord stored into _myRecord ARC will keep the referenced ODRecord alive at least as long as _myRecord exists and the reference is not overwritten by a different reference or nil. It is "at least as long" as the same reference could be stored elsewhere and these will also effect the lifetime.
Almost there! To return a reference to an ODRecord via a parameter the type of the parameter must be "pointer to a variable of type strong reference to ODRecord, i.e.:
- (NSString *)getADrecord:(ODAttributeType)attr
fromRecord:(ODRecord * __strong *)record
now an assignment such as:
*record = item;
will result in an assignment to the pointed-at variable and as that variable is of type ODRecord __strong * ARC will ensure the referenced ODRecord will live at least as long as a reference to it is stored in the pointed-at variable.
Your call to this method must pass a pointer to your variable:
[self getADrecord:attr toRecord:&_myRecord];
Notes:
"out" parameters are not often used in Objective-C with the notable exception of error returns – these are of type NSError * _autoreleasing * and Apple names this usage as "call-by-writeback".
For a deeper explanation of ARC and returning values via parameters see Handling Pointer-to-Pointer Ownership issues in ARC and NSError and __autoreleasing
Important:
As pointed out by #matt in the comments your code contains retain and autorelease calls which are forbidden in ARC and therefore if your code is compiling you DO NOT have ARC enabled. For new projects ARC will be enabled, for existing projects you may need to enable it your project's Build Settings, the setting is called "Objective-C Automatic Reference Counting".
A call to "autorelease" means the object has an additional retain count that will go away when you leave the current autorelease scope, which is typically when the current event is finished.
record = item is obviously not enough, because record's retain count goes away when records leaves scope, that is when the function returns.
But what you do - calling autorelease for each item makes sure that all the items remain allocated for a while, not just "record".

Why is NSArray mutable when used from Swift?

I have an objective-c header with the following property
#property (nullable, nonatomic, strong) NSArray<CustomObject *> *customObjects;
If I create a swift extension of that class I can now remove objects from the NSArray:
self.customObjects?.remove(at: 0)
Also if I do
print(type(of: self.customObjects))
I get:
Array<CustomObject>
Aren't NSArrays immutable ? Does Swift create a shallow copy whenever we edit it?
Your property is (implicitly) declared readwrite in ObjC. This means you can change the property writing a new NSArray instance that replaces the old (in which case the new instance's constants might be derived by first reading the other NSArray instance that's the existing value of the property):
NSArray *currentObjects = self.customObjects;
// one of many ways to derive one immutable array from another:
NSArray *newArray = [currentObjects subarrayWithRange:NSMakeRange(1, currentObjects.count - 1)];
self.customObjects = newArray;
In Swift, your property comes across as a Swift.Array (that is, the Array type from the Swift standard library), which is a value type. Every assignment semantically creates a copy. (The expensive work of performing the copy can be deferred, using a "copy on write" pattern. Arrays of reference types, like objects, copy references instead of storage, so it's essentially a "shallow copy".)
Mutating operations do this, too:
let currentObjects1 = self.customObjects
currentObjects1.remove(0) // compile error
// currentObjects1 is a `let` constant so you can't mutate it
var currentObjects = self.customObjects
currentObjects.remove(0) // ok
print(self.customObjects.count - currentObjects.count)
// this is 1, because currentObjects is a copy of customObjects
// we mutated the former but not the latter so their count is different
self.customObjects = currentObjects
// now we've replaced the original with the mutated copy just as in the ObjC example
When you have a readwrite property in Swift, and the type of that property is a value type like Array (or is an ObjC type that's bridged to a value type, like NSArray), you can use mutating methods directly on the property. That's because calling a mutating method is semantically equivalent to reading (and copying) the existing value, mutating the copy, and then writing back the changed copy.
// all equivalent
self.customObjects.remove(0)
self.customObjects = self.customObjects.dropFirst(1)
var objects = self.customObjects; objects.remove(0); self.customObjects = objects
BTW: If you're designing the API for the ObjC class in question here, you might consider making your customObjects property nonnull — unless there's a meaningful semantic difference between an empty array and a missing array, your Swift clients will find it cumbersome needing to distinguish the two.

AFNetworking: is it safe to access variable outside block?

I'm trying to debug an issue when storing variable outside a block.
- (void) setObj : (NSString *) abc {
[self postURL:#"..." params:#{"abc" : abc} completionHandler:^(id response) {
[[SharedPref sharedInstance] setX:response];
[[SharedPref sharedInstance] setAbc:abc]; <-- can we safely do this?
} failureHandler:^(SBError *error) {
}];
}
I've seen cases where when in later time I try to access abc, I'm getting empty string.
[[SharedPref sharedInstance] getAbc]; <-- this return empty string
It should be safe as long as the abc property in SharedPref is strong or copy. For NSString* types, it's preferred to use copy.
#interface SharedPref : NSObject
#property (copy,nonatomic) NSString* abc;
#end
The difference is as follows:
strong:
strong indicates that the class owns the property
strong increases the reference count of the property by 1
instance will not be released until its reference count is 0.
copy
copy assigns a shallow copy when assigning the property by calling [copy]
copy ensures that you're always dealing with an immutable property. If a mutable property is passed in, it will copy it. If a immutable property is passed in, it will retain it (you would need to dealloc it).
There is nothing wrong with what you are doing. The local variable abc (which is a pointer to an object) is captured by the block and abc inside the block will be a pointer to the same object. Assuming it's an immutable string or you never mutate the string, it should be the same string that is passed to setAbc:.
Given that postURL: is an asynchronous operation (i.e. the completion block is called at some undetermined later time), I am suspecting that you are making false assumptions about the ordering of operations. The completion block that does setAbc: might not have been called yet by the time you do getAbc, and so what you get is the initial value before you set it.

Trouble transferring data between view controllers contained in TabBarController

This is my first question and I am very new in programming field. I have a tab bar controller and I want to transfer data from FVC(1st view controller) to SVC(second View Controller). In FVC ,I am taking controllers contained in tab bar controller in an array (VCArray) and assigning the second object of that array to instance of SVC and setting properties of SVC with appropriate data of FVC but those properties appears nil in SVC.
and 1 more interesting thing is that when I check the SVC instance which was assigned as 2nd object of the VCArray with [isOFKindClass SVC] and [isOFKindClass FVC] both come true..How is it possible? An object can have two classes? and if I check [isOfKIndClass NSArray] it comes false..it means theres nothing wrong in implementation.
Sorry about my bad english..:p
The answer to part1 is that you need to expose properties on SVC, so that another class can access them, and the FVC needs to import the interface file (the .h) of svc. Thus you almost always need two things to do this: a way to find the class you want to make changes to, and the interface of that class. The property will look like this:
SVC.h:
#property (nonatomic, strong) NSString *title;
- (void)doSomething;
FVC:
#import "SVC.h"
SVC *svc = ...; // get a reference to it
svc.title = #"Howdie!";
[svc doSomething]; // tell the class to use the title you just set, for example
To answer your second question, there are two types of these "is.." methods, isKindOfClass and isMemberOfClass. The first says is the current object a type of the provided class, or ANY superclass. The second only passes if the class is in fact an exact member. For example:
NSMutableData *data;
[data isKindOfClass:[NSData class]] == YES
[data isKindOfClass:[NSMutableData class]] == YES
[data isMemeberOfClass:[NSData class]] == NO
[data isMemeberOfClass:[NSMutableData class]] == YES
EDIT: So the data never makes it into SVC. Well try this - a property is just a shortcut to have an ivar, a getter, (and usually) a setter. You can actually provide your own setter. So you say that (using my example above), that in SVC title is always nil, even though its set by SVC. There are only three reasons this can happen:
FVC has a reference to another object, but in fact you called it SVC so when the value is set, its set to another class not SVC
SVC was a nil object when FVC set the value (ObjectiveC handles messages to nil just fine, so you will not see any errors on the console)
SVC has reset the value to nil unbeknownst to you in say viewWillAppear
So the way you can find this out is override the variable setter (again using my example):
- (void)setTitle:(NSString *)val
{
title = val; // ARC way
NSLog(#"SVC - just set title to %#", title);
}
Add this to SVC and see what happens.

Is my understanding of 'self' correct?

I'll provide a simple method and then explain how I see it, if this is incorrect, please let me know and correct me. I feel like I understand 'self' but still doubt my self.
-(NSString *)giveBack {
NSString *string = [NSString stringWithFormat:#"Hi there!"];
return string;
}
-(IBAction)displayIt {
NSString *object = [self giveBack];
[myView setText:object];
}
the "myView" is a UITextView object.
Now as for the 'self'..
I'm basically saying in my -displayIt method that I'm creating a NSString object called 'object' and storing within it a method that returns a string which says "Hi there".
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project). Is this correct?
No, you are not creating an object called object and then storing a method within it etc. You are creating a variable which can hold a reference to an object and storing within it a reference to an object obtained by calling a method.
[Note: The following assumes you are using automatic memory management (ARC or garbage collection), no mention will be made of reference counts. If you are using manual memoery there is more to consider...]
Adding line numbers to your sample:
1. -(NSString *)giveBack
{
2. NSString *string = [NSString stringWithFormat:#"Hi there!"];
3. return string;
}
4. -(IBAction)displayIt
{
5. NSString *object = [self giveBack];
6. [myView setText:object];
}
Declares giveBack as an instance method of the class, to be invoked it must be called on a particular instance.
The RHS ([NSString stringWithFormat:#"Hi there!"]) calls a class method which creates an object of type NSString and returns a reference, of type NSString *, to that object. The LHS declares a variable (string) which can hold a reference to an NSString object. The assignment (=) stores the reference returned by the RHS into the variable declared by the LHS.
Return the value in string as the result of the method
Declare an instance method called displayIt
RHS: call an instance method (giveBack) on the object instance self - self is a reference to the current object instance when within an instance method (in this case displayIt). LHS: declare a variable, object of type NSString *. Assignment: store the reference to an NSString returned by the method call on the RHS into the variable declared on the LHS.
Call the instance method setText: on the object instance referenced by the variable myView passing it the reference to an NSString found in variable object.
I think, you are generally correct.
But in below mention:
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project)
I can't understand your meaning.
A class name is just a symbol (that is text for human readers).
Methods of an Objective-C class are indicated by - notation in the beginning of method declaration.
In other words, all method declarations start with - within #implementation CLASS_NAME ... #end block are instance method of CLASS_NAME class.
When we call another instance methods (within a instance method) we use self keyword. Because all Objective C method call must designate target object and, in this case, we are calling ourselves (current CLASS_NAME instance itself). So we use self keyword.
Sorry for my confusing words.. It's harder to explain I thought :-(
you're storing the string returned by 'giveBack', not the method itself. the method is part of the class. 'self' is the instance of the object that you're calling 'giveBack' (and 'displayIt' for that matter) on.